optimizer/optimizer.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "optimizer.h" | ||
| 2 | #include "ps/local_shm/local_shm_stage_report.h" | ||
| 3 | #include <algorithm> | ||
| 4 | #include <cstring> | ||
| 5 | #include <limits> | ||
| 6 | |||
| 7 | namespace { | ||
| 8 | |||
| 9 | ✗ | std::vector<uint64_t> CollectReaderKeys(const ParameterCompressReader* reader) { | |
| 10 | ✗ | const int size = reader->item_size(); | |
| 11 | ✗ | std::vector<uint64_t> keys; | |
| 12 | ✗ | keys.reserve(size); | |
| 13 | ✗ | for (int i = 0; i < size; ++i) { | |
| 14 | ✗ | keys.push_back(reader->item(i)->key); | |
| 15 | } | ||
| 16 | ✗ | return keys; | |
| 17 | ✗ | } | |
| 18 | |||
| 19 | 14 | void ValidateFlatUpdateArgs(const base::ConstArray<uint64_t>& keys, | |
| 20 | const float* grads, | ||
| 21 | int64_t num_rows, | ||
| 22 | int64_t embedding_dim) { | ||
| 23 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | if (grads == nullptr) { |
| 24 | ✗ | throw std::runtime_error("UpdateFlat grads pointer is null"); | |
| 25 | } | ||
| 26 |
2/4✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
|
14 | if (num_rows < 0 || embedding_dim <= 0) { |
| 27 | ✗ | throw std::runtime_error("UpdateFlat invalid rows/dim"); | |
| 28 | } | ||
| 29 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
|
14 | if (keys.Size() != static_cast<size_t>(num_rows)) { |
| 30 | ✗ | throw std::runtime_error("UpdateFlat keys size mismatch"); | |
| 31 | } | ||
| 32 | 14 | } | |
| 33 | |||
| 34 | } // namespace | ||
| 35 | |||
| 36 | 36 | std::unique_ptr<Optimizer> CreateOptimizer(const json& config) { | |
| 37 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
|
36 | if (!config.is_object()) { |
| 38 | ✗ | throw std::invalid_argument("cache_ps.optimizer must be an object"); | |
| 39 | } | ||
| 40 | |||
| 41 |
1/2✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
|
36 | const std::string type = config.value("type", "SGD"); |
| 42 |
1/2✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
|
36 | const float learning_rate = config.value("learning_rate", 0.01f); |
| 43 |
3/6✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 36 times.
|
36 | if (!std::isfinite(learning_rate) || learning_rate < 0.0f) { |
| 44 | ✗ | throw std::invalid_argument( | |
| 45 | ✗ | "cache_ps.optimizer.learning_rate must be finite and non-negative"); | |
| 46 | } | ||
| 47 | |||
| 48 |
2/2✓ Branch 1 taken 30 times.
✓ Branch 2 taken 6 times.
|
36 | if (type == "SGD") { |
| 49 |
3/6✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30 times.
✗ Branch 8 not taken.
|
60 | LOG(INFO) << "Configured sparse optimizer: type=SGD learning_rate=" |
| 50 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | << learning_rate; |
| 51 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | return std::make_unique<SGD>(learning_rate); |
| 52 | } | ||
| 53 | |||
| 54 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
|
6 | if (type == "RowWiseAdagrad") { |
| 55 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | const float epsilon = config.value("epsilon", 1e-10f); |
| 56 |
3/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
|
2 | if (!std::isfinite(epsilon) || epsilon < 0.0f) { |
| 57 | ✗ | throw std::invalid_argument( | |
| 58 | ✗ | "cache_ps.optimizer.epsilon must be finite and non-negative"); | |
| 59 | } | ||
| 60 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
4 | LOG(INFO) << "Configured sparse optimizer: type=RowWiseAdagrad " |
| 61 |
5/10✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
|
2 | << "learning_rate=" << learning_rate << " epsilon=" << epsilon; |
| 62 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | return std::make_unique<RowWiseAdaGrad>(learning_rate, epsilon); |
| 63 | } | ||
| 64 | |||
| 65 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
|
4 | if (type == "AdamW") { |
| 66 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | const float beta1 = config.value("beta1", 0.9f); |
| 67 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | const float beta2 = config.value("beta2", 0.98f); |
| 68 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | const float epsilon = config.value("epsilon", 1e-8f); |
| 69 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | const float weight_decay = config.value("weight_decay", 0.0f); |
| 70 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
4 | if (!std::isfinite(beta1) || beta1 < 0.0f || beta1 >= 1.0f || |
| 71 |
5/10✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
|
4 | !std::isfinite(beta2) || beta2 < 0.0f || beta2 >= 1.0f) { |
| 72 | ✗ | throw std::invalid_argument( | |
| 73 | ✗ | "cache_ps.optimizer AdamW beta1/beta2 must be finite in [0, 1)"); | |
| 74 | } | ||
| 75 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
4 | if (!std::isfinite(epsilon) || epsilon < 0.0f || |
| 76 |
4/8✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
|
4 | !std::isfinite(weight_decay) || weight_decay < 0.0f) { |
| 77 | ✗ | throw std::invalid_argument( | |
| 78 | "cache_ps.optimizer AdamW epsilon/weight_decay must be finite and " | ||
| 79 | ✗ | "non-negative"); | |
| 80 | } | ||
| 81 |
3/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
|
4 | LOG(INFO) << "Configured sparse optimizer: type=AdamW learning_rate=" |
| 82 |
5/10✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
|
2 | << learning_rate << " beta1=" << beta1 << " beta2=" << beta2 |
| 83 |
4/8✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
|
2 | << " epsilon=" << epsilon << " weight_decay=" << weight_decay; |
| 84 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
4 | return std::make_unique<AdamW>( |
| 85 | 2 | learning_rate, beta1, beta2, epsilon, weight_decay); | |
| 86 | } | ||
| 87 | |||
| 88 |
2/4✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
|
2 | throw std::invalid_argument("Unsupported cache_ps.optimizer.type: " + type); |
| 89 | 36 | } | |
| 90 | |||
| 91 | 24 | void SGD::Init(const std::vector<std::string> table_name, | |
| 92 | const EmbeddingTableConfig& config, | ||
| 93 | BaseKV* base_kv) { | ||
| 94 |
4/8✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 24 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 24 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 24 times.
✗ Branch 13 not taken.
|
24 | LOG(INFO) << "SGD::Init called with " << table_name.size() << " table(s)"; |
| 95 |
2/2✓ Branch 5 taken 24 times.
✓ Branch 6 taken 24 times.
|
48 | for (const auto& name : table_name) { |
| 96 |
5/10✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 24 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 24 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 24 times.
✗ Branch 14 not taken.
|
48 | LOG(INFO) << " Initializing table: '" << name << "' with shape [" |
| 97 |
4/8✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 24 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 24 times.
✗ Branch 11 not taken.
|
24 | << config.num_embeddings << ", " << config.embedding_dim << "]"; |
| 98 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | SparseTensor* param_tensor = new SparseTensor(); |
| 99 |
1/2✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
|
24 | std::vector<uint64_t> shape = {config.num_embeddings, config.embedding_dim}; |
| 100 | 24 | TAG_TYPE tag = 0; // PARAMETER tag | |
| 101 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | param_tensor->init( |
| 102 | const_cast<std::string&>(name), PARAMETER, tag, shape, base_kv); | ||
| 103 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | tensor_map_[name] = param_tensor; |
| 104 | 24 | } | |
| 105 |
3/6✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 24 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 24 times.
✗ Branch 10 not taken.
|
48 | LOG(INFO) << "SGD::Init completed. tensor_map_ now has " << tensor_map_.size() |
| 106 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | << " entries"; |
| 107 | 24 | } | |
| 108 | |||
| 109 | ✗ | void SGD::Update( | |
| 110 | std::string table, const ParameterCompressReader* reader, unsigned tid) { | ||
| 111 | ✗ | auto it = tensor_map_.find(table); | |
| 112 | ✗ | if (it == tensor_map_.end()) { | |
| 113 | ✗ | LOG(ERROR) << "Table not found in SGD optimizer: '" << table << "'"; | |
| 114 | ✗ | throw std::runtime_error("Table not found: " + table); | |
| 115 | } | ||
| 116 | |||
| 117 | ✗ | int size = reader->item_size(); | |
| 118 | ✗ | std::vector<uint64_t> keys = CollectReaderKeys(reader); | |
| 119 | |||
| 120 | ✗ | std::vector<base::ConstArray<float>> current_values; | |
| 121 | ✗ | it->second->BatchGet(keys, ¤t_values, tid); | |
| 122 | |||
| 123 | ✗ | for (int i = 0; i < size; ++i) { | |
| 124 | ✗ | const auto* item = reader->item(i); | |
| 125 | ✗ | if (current_values[i].Size() == 0) { | |
| 126 | // If key not found, we fallback to Put to initialize it | ||
| 127 | ✗ | std::vector<float> zero_init(item->dim, 0.0f); | |
| 128 | ✗ | for (int j = 0; j < item->dim; ++j) { | |
| 129 | ✗ | zero_init[j] = -learning_rate_ * item->data()[j]; | |
| 130 | } | ||
| 131 | std::string val_str( | ||
| 132 | ✗ | (char*)zero_init.data(), zero_init.size() * sizeof(float)); | |
| 133 | ✗ | it->second->Put(item->key, val_str, tid); | |
| 134 | ✗ | continue; | |
| 135 | ✗ | } | |
| 136 | |||
| 137 | ✗ | float* data = const_cast<float*>(current_values[i].Data()); | |
| 138 | ✗ | int dim = std::min(current_values[i].Size(), item->dim); | |
| 139 | |||
| 140 | ✗ | #pragma omp simd | |
| 141 | for (int j = 0; j < dim; ++j) { | ||
| 142 | ✗ | data[j] -= learning_rate_ * item->data()[j]; | |
| 143 | } | ||
| 144 | } | ||
| 145 | ✗ | } | |
| 146 | |||
| 147 | 6 | void SGD::UpdateFlat( | |
| 148 | std::string table, | ||
| 149 | const base::ConstArray<uint64_t>& keys, | ||
| 150 | const float* grads, | ||
| 151 | int64_t num_rows, | ||
| 152 | int64_t embedding_dim, | ||
| 153 | unsigned tid) { | ||
| 154 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | ValidateFlatUpdateArgs(keys, grads, num_rows, embedding_dim); |
| 155 | |||
| 156 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | auto it = tensor_map_.find(table); |
| 157 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | if (it == tensor_map_.end()) { |
| 158 | ✗ | LOG(ERROR) << "Table not found in SGD optimizer: '" << table << "'"; | |
| 159 | ✗ | throw std::runtime_error("Table not found: " + table); | |
| 160 | } | ||
| 161 |
2/4✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
|
6 | if (it->second->EmbeddingDim() != embedding_dim) { |
| 162 | ✗ | throw std::runtime_error( | |
| 163 | ✗ | "SGD::UpdateFlat embedding_dim mismatch for table " + table); | |
| 164 | } | ||
| 165 | |||
| 166 | 6 | const auto direct_update_start = std::chrono::steady_clock::now(); | |
| 167 |
2/4✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
|
6 | if (it->second->ApplySgdUpdateFlat( |
| 168 | keys, grads, num_rows, embedding_dim, learning_rate_, tid)) { | ||
| 169 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | recstore::ReportLocalShmStageMetric( |
| 170 | "sgd_update_direct_us", | ||
| 171 | recstore::LocalShmElapsedUs(direct_update_start)); | ||
| 172 | 6 | return; | |
| 173 | } | ||
| 174 | |||
| 175 | ✗ | std::vector<uint64_t> key_vec(keys.Data(), keys.Data() + keys.Size()); | |
| 176 | ✗ | const auto batch_get_start = std::chrono::steady_clock::now(); | |
| 177 | ✗ | std::vector<base::ConstArray<float>> current_values; | |
| 178 | ✗ | it->second->BatchGet(key_vec, ¤t_values, tid); | |
| 179 | ✗ | recstore::ReportLocalShmStageMetric( | |
| 180 | "sgd_update_batch_get_us", recstore::LocalShmElapsedUs(batch_get_start)); | ||
| 181 | |||
| 182 | ✗ | const auto apply_start = std::chrono::steady_clock::now(); | |
| 183 | ✗ | int64_t missing_rows = 0; | |
| 184 | ✗ | for (int64_t row = 0; row < num_rows; ++row) { | |
| 185 | ✗ | const float* row_grad = grads + row * embedding_dim; | |
| 186 | ✗ | const auto& current = current_values[static_cast<size_t>(row)]; | |
| 187 | ✗ | if (current.Size() == 0) { | |
| 188 | ✗ | ++missing_rows; | |
| 189 | ✗ | std::vector<float> zero_init(static_cast<size_t>(embedding_dim), 0.0f); | |
| 190 | ✗ | for (int64_t col = 0; col < embedding_dim; ++col) { | |
| 191 | ✗ | zero_init[static_cast<size_t>(col)] = -learning_rate_ * row_grad[col]; | |
| 192 | } | ||
| 193 | ✗ | std::string val_str(reinterpret_cast<char*>(zero_init.data()), | |
| 194 | ✗ | zero_init.size() * sizeof(float)); | |
| 195 | ✗ | it->second->Put(keys[static_cast<size_t>(row)], val_str, tid); | |
| 196 | ✗ | continue; | |
| 197 | ✗ | } | |
| 198 | ✗ | if (static_cast<int64_t>(current.Size()) != embedding_dim) { | |
| 199 | ✗ | throw std::runtime_error( | |
| 200 | ✗ | "SGD::UpdateFlat embedding_dim mismatch for table " + table); | |
| 201 | } | ||
| 202 | |||
| 203 | ✗ | float* data = const_cast<float*>(current.Data()); | |
| 204 | ✗ | #pragma omp simd | |
| 205 | for (int64_t col = 0; col < embedding_dim; ++col) { | ||
| 206 | ✗ | data[col] -= learning_rate_ * row_grad[col]; | |
| 207 | } | ||
| 208 | } | ||
| 209 | ✗ | recstore::ReportLocalShmStageMetric( | |
| 210 | "sgd_update_apply_us", recstore::LocalShmElapsedUs(apply_start)); | ||
| 211 | ✗ | recstore::ReportLocalShmStageMetric( | |
| 212 | "sgd_update_missing_rows", static_cast<double>(missing_rows)); | ||
| 213 | ✗ | } | |
| 214 | |||
| 215 | ✗ | void AdaGrad::Init(const std::vector<std::string> table_name, | |
| 216 | const EmbeddingTableConfig& config, | ||
| 217 | BaseKV* base_kv) { | ||
| 218 | ✗ | for (const auto& name : table_name) { | |
| 219 | ✗ | SparseTensor* param_tensor = new SparseTensor(); | |
| 220 | ✗ | std::vector<uint64_t> shape = {config.num_embeddings, config.embedding_dim}; | |
| 221 | ✗ | TAG_TYPE tag = 0; | |
| 222 | ✗ | param_tensor->init( | |
| 223 | const_cast<std::string&>(name), PARAMETER, tag, shape, base_kv); | ||
| 224 | ✗ | tensor_map_[name] = param_tensor; | |
| 225 | |||
| 226 | ✗ | std::string acc_table_name = name + "_accumulated_grad"; | |
| 227 | ✗ | SparseTensor* acc_tensor = new SparseTensor(); | |
| 228 | ✗ | acc_tensor->init( | |
| 229 | const_cast<std::string&>(acc_table_name), | ||
| 230 | MOMENT_1, | ||
| 231 | tag, | ||
| 232 | shape, | ||
| 233 | base_kv); | ||
| 234 | ✗ | tensor_map_[acc_table_name] = acc_tensor; | |
| 235 | ✗ | } | |
| 236 | ✗ | } | |
| 237 | |||
| 238 | ✗ | void AdaGrad::Update( | |
| 239 | std::string table, const ParameterCompressReader* reader, unsigned tid) { | ||
| 240 | ✗ | auto param_it = tensor_map_.find(table); | |
| 241 | ✗ | if (param_it == tensor_map_.end()) { | |
| 242 | ✗ | throw std::runtime_error("Table not found: " + table); | |
| 243 | } | ||
| 244 | |||
| 245 | ✗ | std::string acc_table = table + "_accumulated_grad"; | |
| 246 | ✗ | auto acc_it = tensor_map_.find(acc_table); | |
| 247 | ✗ | if (acc_it == tensor_map_.end()) { | |
| 248 | ✗ | throw std::runtime_error( | |
| 249 | ✗ | "Accumulated gradient table not found: " + acc_table); | |
| 250 | } | ||
| 251 | |||
| 252 | ✗ | int size = reader->item_size(); | |
| 253 | ✗ | std::vector<uint64_t> keys = CollectReaderKeys(reader); | |
| 254 | |||
| 255 | ✗ | std::vector<base::ConstArray<float>> current_values; | |
| 256 | ✗ | std::vector<base::ConstArray<float>> acc_values; | |
| 257 | ✗ | param_it->second->BatchGet(keys, ¤t_values, tid); | |
| 258 | ✗ | acc_it->second->BatchGet(keys, &acc_values, tid); | |
| 259 | |||
| 260 | ✗ | for (int i = 0; i < size; ++i) { | |
| 261 | ✗ | const auto* item = reader->item(i); | |
| 262 | ✗ | if (current_values[i].Size() == 0 || acc_values[i].Size() == 0) { | |
| 263 | // Fallback to sequential initialization if not found | ||
| 264 | // (This is rare in training but kept for robustness) | ||
| 265 | ✗ | continue; | |
| 266 | } | ||
| 267 | |||
| 268 | ✗ | float* param_data = const_cast<float*>(current_values[i].Data()); | |
| 269 | ✗ | float* acc_data = const_cast<float*>(acc_values[i].Data()); | |
| 270 | ✗ | int dim = std::min(current_values[i].Size(), item->dim); | |
| 271 | |||
| 272 | ✗ | #pragma omp simd | |
| 273 | for (int j = 0; j < dim; ++j) { | ||
| 274 | ✗ | acc_data[j] += item->data()[j] * item->data()[j]; | |
| 275 | ✗ | float adaptive_lr = learning_rate_ / (std::sqrt(acc_data[j]) + epsilon_); | |
| 276 | ✗ | param_data[j] -= adaptive_lr * item->data()[j]; | |
| 277 | } | ||
| 278 | } | ||
| 279 | ✗ | } | |
| 280 | |||
| 281 | ✗ | void AdaGrad::UpdateFlat( | |
| 282 | std::string table, | ||
| 283 | const base::ConstArray<uint64_t>& keys, | ||
| 284 | const float* grads, | ||
| 285 | int64_t num_rows, | ||
| 286 | int64_t embedding_dim, | ||
| 287 | unsigned tid) { | ||
| 288 | ✗ | ValidateFlatUpdateArgs(keys, grads, num_rows, embedding_dim); | |
| 289 | |||
| 290 | ✗ | auto param_it = tensor_map_.find(table); | |
| 291 | ✗ | if (param_it == tensor_map_.end()) { | |
| 292 | ✗ | throw std::runtime_error("Table not found: " + table); | |
| 293 | } | ||
| 294 | |||
| 295 | ✗ | std::string acc_table = table + "_accumulated_grad"; | |
| 296 | ✗ | auto acc_it = tensor_map_.find(acc_table); | |
| 297 | ✗ | if (acc_it == tensor_map_.end()) { | |
| 298 | ✗ | throw std::runtime_error( | |
| 299 | ✗ | "Accumulated gradient table not found: " + acc_table); | |
| 300 | } | ||
| 301 | |||
| 302 | ✗ | std::vector<uint64_t> key_vec(keys.Data(), keys.Data() + keys.Size()); | |
| 303 | ✗ | std::vector<base::ConstArray<float>> current_values; | |
| 304 | ✗ | std::vector<base::ConstArray<float>> acc_values; | |
| 305 | ✗ | param_it->second->BatchGet(key_vec, ¤t_values, tid); | |
| 306 | ✗ | acc_it->second->BatchGet(key_vec, &acc_values, tid); | |
| 307 | |||
| 308 | ✗ | for (int64_t row = 0; row < num_rows; ++row) { | |
| 309 | ✗ | const auto& current = current_values[static_cast<size_t>(row)]; | |
| 310 | ✗ | const auto& acc = acc_values[static_cast<size_t>(row)]; | |
| 311 | ✗ | if (current.Size() == 0 || acc.Size() == 0) { | |
| 312 | ✗ | continue; | |
| 313 | } | ||
| 314 | ✗ | if (static_cast<int64_t>(current.Size()) != embedding_dim || | |
| 315 | ✗ | static_cast<int64_t>(acc.Size()) != embedding_dim) { | |
| 316 | ✗ | throw std::runtime_error( | |
| 317 | ✗ | "AdaGrad::UpdateFlat embedding_dim mismatch for table " + table); | |
| 318 | } | ||
| 319 | |||
| 320 | ✗ | const float* row_grad = grads + row * embedding_dim; | |
| 321 | ✗ | float* param_data = const_cast<float*>(current.Data()); | |
| 322 | ✗ | float* acc_data = const_cast<float*>(acc.Data()); | |
| 323 | ✗ | #pragma omp simd | |
| 324 | for (int64_t col = 0; col < embedding_dim; ++col) { | ||
| 325 | ✗ | acc_data[col] += row_grad[col] * row_grad[col]; | |
| 326 | float adaptive_lr = | ||
| 327 | ✗ | learning_rate_ / (std::sqrt(acc_data[col]) + epsilon_); | |
| 328 | ✗ | param_data[col] -= adaptive_lr * row_grad[col]; | |
| 329 | } | ||
| 330 | } | ||
| 331 | ✗ | } | |
| 332 | |||
| 333 | 2 | void RowWiseAdaGrad::Init(const std::vector<std::string> table_name, | |
| 334 | const EmbeddingTableConfig& config, | ||
| 335 | BaseKV* base_kv) { | ||
| 336 |
2/2✓ Branch 5 taken 2 times.
✓ Branch 6 taken 2 times.
|
4 | for (const auto& name : table_name) { |
| 337 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | SparseTensor* param_tensor = new SparseTensor(); |
| 338 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | std::vector<uint64_t> shape = {config.num_embeddings, config.embedding_dim}; |
| 339 | 2 | TAG_TYPE tag = 0; | |
| 340 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | param_tensor->init( |
| 341 | const_cast<std::string&>(name), PARAMETER, tag, shape, base_kv); | ||
| 342 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | tensor_map_[name] = param_tensor; |
| 343 | |||
| 344 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | std::string acc_table_name = name + "_rowwise_accumulated_grad"; |
| 345 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | SparseTensor* acc_tensor = new SparseTensor(); |
| 346 | std::vector<uint64_t> acc_shape = { | ||
| 347 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | config.num_embeddings, 1}; // One value per row |
| 348 | 2 | TAG_TYPE acc_tag = static_cast<TAG_TYPE>(MOMENT_1); | |
| 349 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | acc_tensor->init( |
| 350 | const_cast<std::string&>(acc_table_name), | ||
| 351 | MOMENT_1, | ||
| 352 | acc_tag, | ||
| 353 | acc_shape, | ||
| 354 | base_kv); | ||
| 355 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | tensor_map_[acc_table_name] = acc_tensor; |
| 356 | 2 | } | |
| 357 | 2 | } | |
| 358 | |||
| 359 | ✗ | void RowWiseAdaGrad::Update( | |
| 360 | std::string table, const ParameterCompressReader* reader, unsigned tid) { | ||
| 361 | ✗ | auto param_it = tensor_map_.find(table); | |
| 362 | ✗ | if (param_it == tensor_map_.end()) { | |
| 363 | ✗ | throw std::runtime_error("Table not found: " + table); | |
| 364 | } | ||
| 365 | |||
| 366 | ✗ | std::string acc_table = table + "_rowwise_accumulated_grad"; | |
| 367 | ✗ | auto acc_it = tensor_map_.find(acc_table); | |
| 368 | ✗ | if (acc_it == tensor_map_.end()) { | |
| 369 | ✗ | throw std::runtime_error( | |
| 370 | ✗ | "Row-wise accumulated gradient table not found: " + acc_table); | |
| 371 | } | ||
| 372 | |||
| 373 | ✗ | int size = reader->item_size(); | |
| 374 | ✗ | std::vector<uint64_t> keys = CollectReaderKeys(reader); | |
| 375 | |||
| 376 | ✗ | std::vector<base::ConstArray<float>> current_values; | |
| 377 | ✗ | std::vector<base::ConstArray<float>> acc_values; | |
| 378 | ✗ | param_it->second->BatchGet(keys, ¤t_values, tid); | |
| 379 | ✗ | acc_it->second->BatchGet(keys, &acc_values, tid); | |
| 380 | |||
| 381 | ✗ | for (int i = 0; i < size; ++i) { | |
| 382 | ✗ | const auto* item = reader->item(i); | |
| 383 | ✗ | const auto& current = current_values[static_cast<size_t>(i)]; | |
| 384 | ✗ | const auto& acc = acc_values[static_cast<size_t>(i)]; | |
| 385 | ✗ | const int64_t expected_dim = param_it->second->EmbeddingDim(); | |
| 386 | ✗ | if (item->dim != expected_dim || | |
| 387 | ✗ | (current.Size() != 0 && current.Size() != expected_dim) || | |
| 388 | ✗ | (acc.Size() != 0 && acc.Size() != 1)) { | |
| 389 | ✗ | throw std::runtime_error( | |
| 390 | ✗ | "RowWiseAdaGrad::Update embedding_dim mismatch for table " + table); | |
| 391 | } | ||
| 392 | ✗ | const int dim = item->dim; | |
| 393 | |||
| 394 | ✗ | float grad_square_mean = 0.0; | |
| 395 | ✗ | #pragma omp simd reduction(+ : grad_square_mean) | |
| 396 | for (int j = 0; j < dim; ++j) { | ||
| 397 | ✗ | grad_square_mean += item->data()[j] * item->data()[j]; | |
| 398 | } | ||
| 399 | ✗ | grad_square_mean /= dim; | |
| 400 | |||
| 401 | ✗ | float accumulated_grad = acc.Size() == 0 ? 0.0f : acc.Data()[0]; | |
| 402 | ✗ | accumulated_grad += grad_square_mean; | |
| 403 | |||
| 404 | const float adaptive_lr = | ||
| 405 | ✗ | learning_rate_ / (std::sqrt(accumulated_grad) + epsilon_); | |
| 406 | ✗ | if (current.Size() == 0) { | |
| 407 | ✗ | std::vector<float> initial_value(static_cast<size_t>(dim), 0.0f); | |
| 408 | ✗ | for (int j = 0; j < dim; ++j) { | |
| 409 | ✗ | initial_value[static_cast<size_t>(j)] = -adaptive_lr * item->data()[j]; | |
| 410 | } | ||
| 411 | const std::string value( | ||
| 412 | ✗ | reinterpret_cast<const char*>(initial_value.data()), | |
| 413 | ✗ | initial_value.size() * sizeof(float)); | |
| 414 | ✗ | param_it->second->Put(item->key, value, tid); | |
| 415 | ✗ | } else { | |
| 416 | ✗ | float* param_data = const_cast<float*>(current.Data()); | |
| 417 | ✗ | #pragma omp simd | |
| 418 | for (int j = 0; j < dim; ++j) { | ||
| 419 | ✗ | param_data[j] -= adaptive_lr * item->data()[j]; | |
| 420 | } | ||
| 421 | } | ||
| 422 | |||
| 423 | ✗ | if (acc.Size() == 0) { | |
| 424 | const std::string value( | ||
| 425 | ✗ | reinterpret_cast<const char*>(&accumulated_grad), sizeof(float)); | |
| 426 | ✗ | acc_it->second->Put(item->key, value, tid); | |
| 427 | ✗ | } else { | |
| 428 | ✗ | const_cast<float*>(acc.Data())[0] = accumulated_grad; | |
| 429 | } | ||
| 430 | } | ||
| 431 | ✗ | } | |
| 432 | |||
| 433 | 4 | void RowWiseAdaGrad::UpdateFlat( | |
| 434 | std::string table, | ||
| 435 | const base::ConstArray<uint64_t>& keys, | ||
| 436 | const float* grads, | ||
| 437 | int64_t num_rows, | ||
| 438 | int64_t embedding_dim, | ||
| 439 | unsigned tid) { | ||
| 440 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | ValidateFlatUpdateArgs(keys, grads, num_rows, embedding_dim); |
| 441 | |||
| 442 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto param_it = tensor_map_.find(table); |
| 443 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | if (param_it == tensor_map_.end()) { |
| 444 | ✗ | throw std::runtime_error("Table not found: " + table); | |
| 445 | } | ||
| 446 | |||
| 447 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | std::string acc_table = table + "_rowwise_accumulated_grad"; |
| 448 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto acc_it = tensor_map_.find(acc_table); |
| 449 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | if (acc_it == tensor_map_.end()) { |
| 450 | ✗ | throw std::runtime_error( | |
| 451 | ✗ | "Row-wise accumulated gradient table not found: " + acc_table); | |
| 452 | } | ||
| 453 | |||
| 454 |
1/2✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
|
4 | std::vector<uint64_t> key_vec(keys.Data(), keys.Data() + keys.Size()); |
| 455 | 4 | std::vector<base::ConstArray<float>> current_values; | |
| 456 | 4 | std::vector<base::ConstArray<float>> acc_values; | |
| 457 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | param_it->second->BatchGet(key_vec, ¤t_values, tid); |
| 458 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | acc_it->second->BatchGet(key_vec, &acc_values, tid); |
| 459 | |||
| 460 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | for (int64_t row = 0; row < num_rows; ++row) { |
| 461 | 4 | const auto& current = current_values[static_cast<size_t>(row)]; | |
| 462 | 4 | const auto& acc = acc_values[static_cast<size_t>(row)]; | |
| 463 | 4 | if ((current.Size() != 0 && | |
| 464 |
6/8✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
|
10 | static_cast<int64_t>(current.Size()) != embedding_dim) || |
| 465 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
|
6 | (acc.Size() != 0 && acc.Size() != 1)) { |
| 466 | ✗ | throw std::runtime_error( | |
| 467 | ✗ | "RowWiseAdaGrad::UpdateFlat embedding_dim mismatch for table " + | |
| 468 | ✗ | table); | |
| 469 | } | ||
| 470 | |||
| 471 | 4 | const float* row_grad = grads + row * embedding_dim; | |
| 472 | 4 | float grad_square_mean = 0.0f; | |
| 473 | 4 | #pragma omp simd reduction(+ : grad_square_mean) | |
| 474 | for (int64_t col = 0; col < embedding_dim; ++col) { | ||
| 475 | 8 | grad_square_mean += row_grad[col] * row_grad[col]; | |
| 476 | } | ||
| 477 | 4 | grad_square_mean /= static_cast<float>(embedding_dim); | |
| 478 | |||
| 479 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
|
4 | float accumulated_grad = acc.Size() == 0 ? 0.0f : acc.Data()[0]; |
| 480 | 4 | accumulated_grad += grad_square_mean; | |
| 481 | const float adaptive_lr = | ||
| 482 | 4 | learning_rate_ / (std::sqrt(accumulated_grad) + epsilon_); | |
| 483 | |||
| 484 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
|
4 | if (current.Size() == 0) { |
| 485 | std::vector<float> initial_value( | ||
| 486 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | static_cast<size_t>(embedding_dim), 0.0f); |
| 487 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
|
6 | for (int64_t col = 0; col < embedding_dim; ++col) { |
| 488 | 4 | initial_value[static_cast<size_t>(col)] = -adaptive_lr * row_grad[col]; | |
| 489 | } | ||
| 490 | const std::string value( | ||
| 491 | 2 | reinterpret_cast<const char*>(initial_value.data()), | |
| 492 |
1/2✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | initial_value.size() * sizeof(float)); |
| 493 |
1/2✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | param_it->second->Put(keys[static_cast<size_t>(row)], value, tid); |
| 494 | 2 | } else { | |
| 495 | 2 | float* param_data = const_cast<float*>(current.Data()); | |
| 496 | 2 | #pragma omp simd | |
| 497 | for (int64_t col = 0; col < embedding_dim; ++col) { | ||
| 498 | 4 | param_data[col] -= adaptive_lr * row_grad[col]; | |
| 499 | } | ||
| 500 | } | ||
| 501 | |||
| 502 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
|
4 | if (acc.Size() == 0) { |
| 503 | const std::string value( | ||
| 504 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | reinterpret_cast<const char*>(&accumulated_grad), sizeof(float)); |
| 505 |
1/2✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | acc_it->second->Put(keys[static_cast<size_t>(row)], value, tid); |
| 506 | 2 | } else { | |
| 507 | 2 | const_cast<float*>(acc.Data())[0] = accumulated_grad; | |
| 508 | } | ||
| 509 | } | ||
| 510 | 4 | } | |
| 511 | |||
| 512 | namespace { | ||
| 513 | |||
| 514 | // The step is stored as a tagged scalar in its own table. Keep its key away | ||
| 515 | // from normal embedding ids (the top 8 bits are reserved for TensorType). | ||
| 516 | constexpr uint64_t kAdamWStepKey = (std::numeric_limits<uint64_t>::max() >> 8); | ||
| 517 | |||
| 518 | } // namespace | ||
| 519 | |||
| 520 | 4 | void AdamW::Init(const std::vector<std::string> table_name, | |
| 521 | const EmbeddingTableConfig& config, | ||
| 522 | BaseKV* base_kv) { | ||
| 523 |
2/2✓ Branch 5 taken 4 times.
✓ Branch 6 taken 4 times.
|
8 | for (const auto& name : table_name) { |
| 524 | const std::vector<uint64_t> shape = { | ||
| 525 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | config.num_embeddings, config.embedding_dim}; |
| 526 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto* param_tensor = new SparseTensor(); |
| 527 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto mutable_name = name; |
| 528 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto mutable_shape = shape; |
| 529 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | param_tensor->init( |
| 530 | mutable_name, PARAMETER, PARAMETER, mutable_shape, base_kv); | ||
| 531 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | tensor_map_[name] = param_tensor; |
| 532 | |||
| 533 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto* first_moment = new SparseTensor(); |
| 534 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | const std::string first_name = name + "_adamw_m"; |
| 535 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto mutable_first_name = first_name; |
| 536 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | first_moment->init( |
| 537 | mutable_first_name, MOMENT_1, MOMENT_1, mutable_shape, base_kv); | ||
| 538 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | tensor_map_[first_name] = first_moment; |
| 539 | |||
| 540 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto* second_moment = new SparseTensor(); |
| 541 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | const std::string second_name = name + "_adamw_v"; |
| 542 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto mutable_second_name = second_name; |
| 543 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | second_moment->init( |
| 544 | mutable_second_name, MOMENT_2, MOMENT_2, mutable_shape, base_kv); | ||
| 545 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | tensor_map_[second_name] = second_moment; |
| 546 | |||
| 547 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto* step_tensor = new SparseTensor(); |
| 548 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | const std::string step_name = name + "_adamw_step"; |
| 549 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto mutable_step_name = step_name; |
| 550 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | std::vector<uint64_t> step_shape = {1, 1}; |
| 551 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | step_tensor->init( |
| 552 | mutable_step_name, MOMENT_1, MOMENT_1, step_shape, base_kv); | ||
| 553 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | tensor_map_[step_name] = step_tensor; |
| 554 | 4 | } | |
| 555 | 4 | } | |
| 556 | |||
| 557 | ✗ | void AdamW::Update( | |
| 558 | std::string table, const ParameterCompressReader* reader, unsigned tid) { | ||
| 559 | ✗ | auto param_it = tensor_map_.find(table); | |
| 560 | ✗ | if (param_it == tensor_map_.end()) { | |
| 561 | ✗ | throw std::runtime_error("Table not found: " + table); | |
| 562 | } | ||
| 563 | ✗ | const int size = reader->item_size(); | |
| 564 | ✗ | const int64_t dim = param_it->second->EmbeddingDim(); | |
| 565 | ✗ | std::vector<uint64_t> keys; | |
| 566 | ✗ | std::vector<float> grads; | |
| 567 | ✗ | keys.reserve(size); | |
| 568 | ✗ | grads.reserve(static_cast<size_t>(size) * static_cast<size_t>(dim)); | |
| 569 | ✗ | for (int i = 0; i < size; ++i) { | |
| 570 | ✗ | const auto* item = reader->item(i); | |
| 571 | ✗ | if (item->dim != dim) { | |
| 572 | ✗ | throw std::runtime_error( | |
| 573 | ✗ | "AdamW::Update embedding_dim mismatch for table " + table); | |
| 574 | } | ||
| 575 | ✗ | keys.push_back(item->key); | |
| 576 | ✗ | grads.insert(grads.end(), item->data(), item->data() + dim); | |
| 577 | } | ||
| 578 | ✗ | UpdateRows(table, keys.data(), grads.data(), size, dim, tid); | |
| 579 | ✗ | } | |
| 580 | |||
| 581 | 4 | void AdamW::UpdateFlat( | |
| 582 | std::string table, | ||
| 583 | const base::ConstArray<uint64_t>& keys, | ||
| 584 | const float* grads, | ||
| 585 | int64_t num_rows, | ||
| 586 | int64_t embedding_dim, | ||
| 587 | unsigned tid) { | ||
| 588 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | ValidateFlatUpdateArgs(keys, grads, num_rows, embedding_dim); |
| 589 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto it = tensor_map_.find(table); |
| 590 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | if (it == tensor_map_.end()) { |
| 591 | ✗ | throw std::runtime_error("Table not found: " + table); | |
| 592 | } | ||
| 593 |
2/4✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
|
4 | if (it->second->EmbeddingDim() != embedding_dim) { |
| 594 | ✗ | throw std::runtime_error( | |
| 595 | ✗ | "AdamW::UpdateFlat embedding_dim mismatch for table " + table); | |
| 596 | } | ||
| 597 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | UpdateRows(table, keys.Data(), grads, num_rows, embedding_dim, tid); |
| 598 | 4 | } | |
| 599 | |||
| 600 | 4 | void AdamW::UpdateRows( | |
| 601 | const std::string& table, | ||
| 602 | const uint64_t* keys, | ||
| 603 | const float* grads, | ||
| 604 | int64_t num_rows, | ||
| 605 | int64_t embedding_dim, | ||
| 606 | unsigned tid) { | ||
| 607 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto param_it = tensor_map_.find(table); |
| 608 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
4 | auto first_it = tensor_map_.find(table + "_adamw_m"); |
| 609 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
4 | auto second_it = tensor_map_.find(table + "_adamw_v"); |
| 610 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
4 | auto step_it = tensor_map_.find(table + "_adamw_step"); |
| 611 |
2/4✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
|
12 | if (param_it == tensor_map_.end() || first_it == tensor_map_.end() || |
| 612 |
3/6✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 4 times.
|
12 | second_it == tensor_map_.end() || step_it == tensor_map_.end()) { |
| 613 | ✗ | throw std::runtime_error("AdamW state table not found for table " + table); | |
| 614 | } | ||
| 615 | |||
| 616 | 4 | std::string step_value; | |
| 617 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | step_it->second->Get(kAdamWStepKey, step_value, tid); |
| 618 | float step = | ||
| 619 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
|
4 | step_value.empty() ? 0.0f : base::ConstArray<float>(step_value).Data()[0]; |
| 620 | 4 | step += 1.0f; | |
| 621 | 4 | const float bias1 = 1.0f - std::pow(beta1_, step); | |
| 622 | 4 | const float bias2 = 1.0f - std::pow(beta2_, step); | |
| 623 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | if (!(bias1 > 0.0f) || !(bias2 > 0.0f)) { |
| 624 | ✗ | throw std::runtime_error("AdamW bias correction underflow"); | |
| 625 | } | ||
| 626 | |||
| 627 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | std::vector<uint64_t> key_vec(keys, keys + num_rows); |
| 628 | 4 | std::vector<base::ConstArray<float>> params; | |
| 629 | 4 | std::vector<base::ConstArray<float>> first; | |
| 630 | 4 | std::vector<base::ConstArray<float>> second; | |
| 631 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | param_it->second->BatchGet(key_vec, ¶ms, tid); |
| 632 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | first_it->second->BatchGet(key_vec, &first, tid); |
| 633 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | second_it->second->BatchGet(key_vec, &second, tid); |
| 634 | |||
| 635 | 4 | const float decay = 1.0f - learning_rate_ * weight_decay_; | |
| 636 | 4 | const float correction1 = 1.0f / bias1; | |
| 637 | 4 | const float correction2 = 1.0f / bias2; | |
| 638 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | for (int64_t row = 0; row < num_rows; ++row) { |
| 639 | 4 | const auto index = static_cast<size_t>(row); | |
| 640 | 4 | const float* row_grad = grads + row * embedding_dim; | |
| 641 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | std::vector<float> param(static_cast<size_t>(embedding_dim), 0.0f); |
| 642 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | std::vector<float> moment1(static_cast<size_t>(embedding_dim), 0.0f); |
| 643 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | std::vector<float> moment2(static_cast<size_t>(embedding_dim), 0.0f); |
| 644 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | if (params[index].Size() != 0) { |
| 645 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | if (params[index].Size() != embedding_dim) { |
| 646 | ✗ | throw std::runtime_error( | |
| 647 | ✗ | "AdamW parameter dimension mismatch for table " + table); | |
| 648 | } | ||
| 649 |
1/2✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
8 | std::copy(params[index].Data(), |
| 650 | 4 | params[index].Data() + embedding_dim, | |
| 651 | param.begin()); | ||
| 652 | } | ||
| 653 |
2/2✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
|
4 | if (first[index].Size() != 0) { |
| 654 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
|
2 | if (first[index].Size() != embedding_dim) { |
| 655 | ✗ | throw std::runtime_error( | |
| 656 | ✗ | "AdamW first moment dimension mismatch for table " + table); | |
| 657 | } | ||
| 658 |
1/2✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
4 | std::copy(first[index].Data(), |
| 659 | 2 | first[index].Data() + embedding_dim, | |
| 660 | moment1.begin()); | ||
| 661 | } | ||
| 662 |
2/2✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
|
4 | if (second[index].Size() != 0) { |
| 663 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
|
2 | if (second[index].Size() != embedding_dim) { |
| 664 | ✗ | throw std::runtime_error( | |
| 665 | ✗ | "AdamW second moment dimension mismatch for table " + table); | |
| 666 | } | ||
| 667 |
1/2✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
4 | std::copy(second[index].Data(), |
| 668 | 2 | second[index].Data() + embedding_dim, | |
| 669 | moment2.begin()); | ||
| 670 | } | ||
| 671 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
|
12 | for (int64_t col = 0; col < embedding_dim; ++col) { |
| 672 | 8 | const float grad = row_grad[col]; | |
| 673 | 16 | moment1[static_cast<size_t>(col)] = | |
| 674 | 8 | beta1_ * moment1[static_cast<size_t>(col)] + (1.0f - beta1_) * grad; | |
| 675 | 16 | moment2[static_cast<size_t>(col)] = | |
| 676 | 8 | beta2_ * moment2[static_cast<size_t>(col)] + | |
| 677 | 8 | (1.0f - beta2_) * grad * grad; | |
| 678 | 8 | const float m_hat = moment1[static_cast<size_t>(col)] * correction1; | |
| 679 | 8 | const float v_hat = moment2[static_cast<size_t>(col)] * correction2; | |
| 680 | 8 | param[static_cast<size_t>(col)] = | |
| 681 | 8 | decay * param[static_cast<size_t>(col)] - | |
| 682 | 8 | learning_rate_ * m_hat / (std::sqrt(v_hat) + epsilon_); | |
| 683 | } | ||
| 684 | 4 | const std::string param_value(reinterpret_cast<const char*>(param.data()), | |
| 685 |
1/2✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
4 | param.size() * sizeof(float)); |
| 686 | 4 | const std::string first_value(reinterpret_cast<const char*>(moment1.data()), | |
| 687 |
1/2✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
4 | moment1.size() * sizeof(float)); |
| 688 | const std::string second_value( | ||
| 689 | 4 | reinterpret_cast<const char*>(moment2.data()), | |
| 690 |
1/2✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
4 | moment2.size() * sizeof(float)); |
| 691 |
1/2✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
4 | param_it->second->Put(keys[index], param_value, tid); |
| 692 |
1/2✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
4 | first_it->second->Put(keys[index], first_value, tid); |
| 693 |
1/2✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
4 | second_it->second->Put(keys[index], second_value, tid); |
| 694 | 4 | } | |
| 695 | const std::string next_step( | ||
| 696 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | reinterpret_cast<const char*>(&step), sizeof(float)); |
| 697 |
1/2✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
4 | step_it->second->Put(kAdamWStepKey, next_step, tid); |
| 698 | 4 | } | |
| 699 |