DiSMEC++
model-io.cpp
Go to the documentation of this file.
1 // Copyright (c) 2021, Aalto University, developed by Erik Schultheis
2 // All rights reserved.
3 //
4 // SPDX-License-Identifier: MIT
5 
6 #include "io/model-io.h"
7 #include "io/common.h"
8 #include "io/weights.h"
9 #include "model/dense.h"
10 #include "model/sparse.h"
11 #include "model/submodel.h"
12 #include <fstream>
13 #include <array>
14 #include <iomanip>
15 #include <utility>
16 #include "spdlog/spdlog.h"
17 #include "spdlog/fmt/fmt.h"
18 #include "spdlog/fmt/chrono.h"
19 #include "nlohmann/json.hpp"
20 #include "parallel/numa.h"
21 
23 using namespace dismec;
24 using namespace dismec::io::model;
25 
26 namespace {
28  const std::array<const char*, 4> weight_format_names = {
29  "DenseTXT", "SparseTXT", "DenseNPY", "<NULL>"
30  };
31 
33  const std::array<bool, 4> weight_format_sparsity = {
34  false, true, false, true
35  };
36 
43  void save_weights_dispatch(std::ostream& target, const Model& model, SaveOption& options) {
44  target.precision(options.Precision);
45  switch (options.Format) {
47  save_dense_weights_txt(target, model);
48  break;
50  save_as_sparse_weights_txt(target, model, options.Culling);
51  break;
53  save_dense_weights_npy(*target.rdbuf(), model);
54  break;
56  return;
57  }
58  }
59 
67  void read_weights_dispatch(std::istream& source, WeightFormat format, Model& model) {
68  switch (format) {
70  load_dense_weights_txt(source, model);
71  break;
73  load_sparse_weights_txt(source, model);
74  break;
76  load_dense_weights_npy(*source.rdbuf(), model);
77  break;
78  default:
79  throw std::runtime_error("Invalid format");
80  }
81  }
82 }
83 
84 WeightFormat io::model::parse_weights_format(std::string_view weight_format) {
85  const auto* format_index = std::find(begin(weight_format_names), end(weight_format_names), weight_format);
86  return static_cast<WeightFormat>(std::distance(begin(weight_format_names), format_index));
87 }
88 
89 const char* io::model::to_string(WeightFormat format) {
90  return weight_format_names.at(static_cast<unsigned long>(format));
91 }
92 
93 void PartialModelIO::read_metadata_file(const path& meta_file) {
94  std::fstream source(meta_file, std::fstream::in);
95  if(!source.is_open()) {
96  throw std::runtime_error(fmt::format("Could not open model metadata file '{}'", meta_file.c_str()));
97  }
98 
99  // read and parse the metadata
100  std::string s;
101  getline (source, s, '\0');
102  json meta = json::parse(s);
103 
104  m_NumFeatures = meta["num-features"];
105  m_TotalLabels = meta["num-labels"];
106 
107  for(auto& weight_file : meta["files"]) {
108  label_id_t first = label_id_t{weight_file["first"]};
109  long count = weight_file["count"];
110  std::string weight_format = weight_file["weight-format"];
111  auto format = parse_weights_format(weight_format);
112  m_SubFiles.push_back(WeightFileEntry{first, count, weight_file["file"], format});
113  }
114 }
115 
116 auto io::model::PartialModelIO::label_lower_bound(label_id_t pos) const -> std::vector<WeightFileEntry>::const_iterator {
117  return std::lower_bound(begin(m_SubFiles), end(m_SubFiles), pos,
118  [](const WeightFileEntry& s, label_id_t val) {
119  return s.First < val;
120  });
121 }
122 
124  auto last_label = [](const WeightFileEntry& sf) {
125  return sf.First + (sf.Count - 1);
126  };
127 
128  auto insert_pos = label_lower_bound(sub.First);
129  // now, insert pos points to the element that will end up following the newly inserted element.
130 
131  // check that there is no overlap between partial models
132  // if there is a successor element
133  if(insert_pos != m_SubFiles.end()) {
134  if(last_label(sub) >= insert_pos->First) {
135  throw std::logic_error(fmt::format("Overlap detected! Partial model in file {} stores weights {}-{}, "
136  "partial model in file {} stores {}-{}", insert_pos->FileName,
137  insert_pos->First.to_index(), last_label(*insert_pos).to_index(), sub.FileName,
138  sub.First.to_index(), last_label(sub).to_index()));
139  }
140 
141  }
142 
143  // if there is a previous element, also check that
144  if(insert_pos != m_SubFiles.begin()) {
145  const auto& prev_el = *std::prev(insert_pos);
146  if(last_label(prev_el) >= sub.First) {
147  throw std::logic_error(fmt::format("Overlap detected! Partial model in file {} stores weights {}-{}, "
148  "partial model in file {} stores {}-{}", prev_el.FileName, prev_el.First.to_index(),
149  last_label(prev_el).to_index(), sub.FileName, sub.First.to_index(), last_label(sub).to_index()));
150  }
151  }
152 
153  m_SubFiles.insert(insert_pos, sub);
154 }
155 
156 // -------------------------------------------------------------------------------
157 // Partial Model Saver
158 // -------------------------------------------------------------------------------
159 
160 PartialModelSaver::PartialModelSaver(path target_file, SaveOption options, bool load_partial) :
161  m_Options(options), m_MetaFileName(std::move(target_file)) {
162  if(load_partial) {
164  }
165  // check validity of save location
166  if(!m_MetaFileName.parent_path().empty() && !std::filesystem::exists(m_MetaFileName.parent_path())) {
167  throw std::runtime_error(fmt::format("Cannot save to '{}' because directory does not exist.",
168  m_MetaFileName.c_str()));
169  }
170 }
171 
172 std::future<WeightFileEntry> PartialModelSaver::add_model(const std::shared_ptr<const Model>& model, const std::optional<std::string>& file_path)
173 {
174  // check compatibility of the partial model
175  // if this is the first partial model, accept the values
176  if(m_TotalLabels == -1) {
177  m_TotalLabels = model->num_labels();
178  m_NumFeatures = model->num_features();
179  } else {
180  // we know what to expect, verify
181  if(m_TotalLabels != model->num_labels()) {
182  throw std::logic_error(fmt::format("Received partial model for {} labels, but expected {} labels",
183  model->num_labels(), m_TotalLabels));
184  }
185  if(m_NumFeatures != model->num_features()) {
186  throw std::logic_error(fmt::format("Received partial model for {} features, but expected {} features",
187  model->num_features(), m_NumFeatures));
188  }
189  }
190 
191  path target_file = file_path.value_or(m_MetaFileName);
192  if(!file_path.has_value()) {
193  target_file.replace_filename(fmt::format("{}.weights-{}-{}", m_MetaFileName.filename().c_str(),
194  model->labels_begin().to_index(), model->labels_end().to_index() - 1));
195  }
196 
197  // find out where to insert this partial model into the ordered list
198  WeightFileEntry new_weights_file{model->labels_begin(), model->num_weights(), target_file.filename(), m_Options.Format};
199 
200  // skip if we don't want to save weights
202  // if we don't actually want to write anything (e.g. for test cases, we just register the sub file and be done)
203  insert_sub_file(new_weights_file);
204  return std::async(std::launch::deferred, [new_weights_file](){
205  return new_weights_file;
206  });
207  }
208 
209  using namespace std::chrono;
210  std::fstream weights_file(target_file, std::fstream::out | std::fstream::binary);
211  if(!weights_file.is_open()) {
212  throw std::runtime_error(fmt::format("Could not create weights file {}", target_file.c_str()));
213  }
214 
215  // add the weights file to the list of weights
216  insert_sub_file(new_weights_file);
217 
218  // OK, now we are sure everything is fine, do the actual work on saving stuff
219  // we do this asynchronous, as we expect this to mostly IO bound
220  return std::async(std::launch::async, [this, target=std::move(weights_file), model, new_weights_file]() mutable
221  {
222  // let the saving thread only run on cores of the numa node which stores the model.
223  // this assumes that the internal buffers of model are on the same node as model.
224  parallel::pin_to_data(model.get());
225  auto now = steady_clock::now();
226  save_weights_dispatch(target, *model, m_Options);
227  spdlog::info("Saving partial model for weights {}-{} took {} ms",
228  model->labels_begin().to_index(), model->labels_begin().to_index() + model->num_weights(),
229  duration_cast<milliseconds>(steady_clock::now() - now).count());
230  return new_weights_file;
231  });
232 }
233 
235  json meta;
236  meta["num-features"] = m_NumFeatures;
237  meta["num-labels"] = m_TotalLabels;
238  std::time_t tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
239  std::tm tm = *std::gmtime(&tt);
240  char date_buffer[128];
241  strftime(date_buffer, sizeof(date_buffer), "%F - %T", &tm);
242  meta["date"] = date_buffer;
243 
244  for(auto& sub : m_SubFiles)
245  {
246  json file_data = {{"first", sub.First.to_index()}, {"count", sub.Count},
247  {"file", sub.FileName}, {"weight-format", to_string(sub.Format)}};
248  meta["files"].push_back(file_data);
249  }
250 
251  std::fstream meta_file(m_MetaFileName, std::fstream::out);
252  meta_file << std::setw(4) << meta << "\n";
253 }
254 
256 {
257  auto iter = label_lower_bound(begin);
258  // check that there is no overlap between partial models
259  // if there is a successor element
260  if(iter != m_SubFiles.end()) {
261  if(end > iter->First) {
262  return true;
263  }
264  }
265 
266  // if there is a previous element, also check that
267  if(iter != m_SubFiles.begin()) {
268  const auto& prev_el = *std::prev(iter);
269  if(prev_el.First + prev_el.Count > begin) {
270  return true;
271  }
272  }
273 
274  return false;
275 }
276 
278  label_id_t last_end{0};
279  for(auto& sub : m_SubFiles) {
280  if(last_end != sub.First) {
281  throw std::logic_error(fmt::format("Some labels are missing. Gap from {} to {}", last_end.to_index(), sub.First.to_index() - 1));
282  }
283  last_end = sub.First + sub.Count;
284  }
285  if(last_end.to_index() != m_TotalLabels) {
286  throw std::logic_error(fmt::format("Some labels are missing. Gap from {} to {}", last_end.to_index(), m_TotalLabels - 1));
287  }
288 
290 }
291 
292 std::pair<label_id_t, label_id_t> PartialModelSaver::get_missing_weights() const {
293  label_id_t last_end{0};
294  label_id_t label_end{m_TotalLabels};
295  for(const auto& sub : m_SubFiles) {
296  if(last_end != sub.First) {
297  return {last_end, sub.First};
298  }
299  last_end = sub.First + sub.Count;
300  }
301  if(last_end != label_end) {
302  return {last_end, label_end};
303  }
304 
305  return {label_end, label_end};
306 }
307 
308 void io::model::save_model(const path& target_file, const std::shared_ptr<const Model>& model, SaveOption options)
309 {
310  if(model->is_partial_model()) {
311  throw std::logic_error("save_model can only save complete models");
312  }
313 
314  PartialModelSaver saver{target_file, options};
315 
316  // TODO more info, i.e. bias, which labels, etc
317  if(model->num_labels() < options.SplitFiles) {
318  saver.add_model(model, fmt::format("{}.weights", target_file.filename().c_str()));
319  } else {
320  int num_files = std::round( model->num_weights() / static_cast<double>(options.SplitFiles) );
321  for(int sub = 0; sub < num_files; ++sub) {
322  std::string weights_file_name = fmt::format("{}.weights-{}-of-{}", target_file.filename().c_str(), 1+sub, num_files);
323  label_id_t first{sub * options.SplitFiles};
324  long end_label = (sub == num_files - 1) ? model->num_weights() : (sub+1) * options.SplitFiles;
325  end_label = std::min(end_label, model->num_labels());
326  auto submodel = std::make_shared<::model::ConstSubModelView>(model.get(), first, label_id_t{end_label});
327  saver.add_model(submodel, weights_file_name);
328  }
329  }
330 
331  saver.finalize();
332 }
333 
334 std::shared_ptr<Model> io::model::load_model(path source)
335 {
336  PartialModelLoader loader{std::move(source)};
337  return loader.load_model(label_id_t{0}, label_id_t{loader.num_labels()});
338 }
339 
340 
342  m_MetaFileName(std::move(meta_file)), m_SparseMode(mode) {
344 }
345 
346 auto PartialModelLoader::get_loading_range(label_id_t label_begin, label_id_t label_end) const ->
348 {
349  auto sub_files = std::equal_range(begin(m_SubFiles), end(m_SubFiles),
350  WeightFileEntry{label_begin, label_end - label_begin, {}, WeightFormat::NULL_FORMAT},
351  [](const WeightFileEntry& left, const WeightFileEntry& right) {
352  auto a1 = left.First;
353  auto a2 = right.First;
354  auto b1 = a1 + left.Count;
355  return b1 <= a2;
356  });
357  if(sub_files.first == end(m_SubFiles)) {
358  throw std::runtime_error(fmt::format("Could not find weights for interval [{}, {})",
359  label_begin.to_index(), label_end.to_index()));
360  }
361 
362  auto calc_label_count = [&](){
363  if(sub_files.second == end(m_SubFiles)) {
364  return label_id_t{m_TotalLabels};
365  } else {
366  return sub_files.second->First;
367  }
368  };
369 
370  return SubModelRangeSpec{sub_files.first, sub_files.second, sub_files.first->First, calc_label_count()};
371 }
372 
373 std::shared_ptr<Model> PartialModelLoader::load_model(label_id_t label_begin, label_id_t label_end) const {
374  auto sub_range = get_loading_range(label_begin, label_end);
375 
376 
377  ::model::PartialModelSpec spec {sub_range.LabelsBegin, sub_range.LabelsEnd - sub_range.LabelsBegin, m_TotalLabels};
378 
379  auto model = std::make_shared<::model::DenseModel>(m_NumFeatures, spec);
380 
381  for(auto file = sub_range.FilesBegin; file < sub_range.FilesEnd; ++file) {
382  ::model::SubModelView submodel{model.get(), file->First, file->First + file->Count};
383  path weights_file = m_MetaFileName;
384  std::fstream source(weights_file.replace_filename(file->FileName), std::fstream::in);
385  if(!source.is_open()) {
386  THROW_ERROR("Could not open weights file ", weights_file.replace_filename(file->FileName).string());
387  }
388  read_weights_dispatch(source, file->Format, submodel);
389  spdlog::info("read weight file {}", weights_file.replace_filename(file->FileName).c_str());
390  }
391 
392  return model;
393 }
394 
396  return m_SubFiles.size();
397 }
398 
399 namespace {
400  std::shared_ptr<Model> make_model(long num_features, ::model::PartialModelSpec spec, bool sparse) {
401  if(sparse) {
402  return std::make_shared<::model::SparseModel>(num_features, spec);
403  } else {
404  return std::make_shared<::model::DenseModel>(num_features, spec);
405  }
406  }
407 
409  switch (mode) {
411  return true;
413  return false;
415  return weight_format_sparsity[static_cast<int>(format)];
416  default:
417  assert(0);
418  __builtin_unreachable();
419  }
420  }
421 }
422 
423 std::shared_ptr<Model> PartialModelLoader::load_model(int index) const {
424  auto start = std::chrono::steady_clock::now();
425  const WeightFileEntry& entry = m_SubFiles.at(index);
426  ::model::PartialModelSpec spec {entry.First, entry.Count, num_labels()};
427 
428  auto model = make_model(num_features(), spec, use_sparse_weights(m_SparseMode, entry.Format));
429  path weights_file = meta_file_path();
430  std::fstream source(weights_file.replace_filename(entry.FileName), std::fstream::in);
431  if(!source.is_open()) {
432  THROW_ERROR("Could not open weights file '{}'", weights_file.replace_filename(entry.FileName).string());
433  }
434  read_weights_dispatch(source, entry.Format, *model);
435  auto duration = std::chrono::steady_clock::now() - start;
436  spdlog::info("read weight file '{}' in {}ms", weights_file.replace_filename(entry.FileName).c_str(),
437  std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
438  return model;
439 }
440 
442  bool valid = true;
443  for(const auto& entry: m_SubFiles) {
444  path weights_file = meta_file_path();
445  auto wf = weights_file.replace_filename(entry.FileName);
446 
447  // check existence
448  if(!std::filesystem::exists(wf)) {
449  spdlog::error("Weight file '{}' does not exist!", wf.string());
450  valid = false;
451  }
452 
453  // check if we can actually open it
454  std::fstream source(weights_file.replace_filename(entry.FileName), std::fstream::in);
455  if(!source.is_open()) {
456  spdlog::error("Could not open weight file '{}'", wf.string());
457  valid = false;
458  }
459  }
460  return valid;
461 }
462 
463 
464 #include "doctest.h"
465 
466 using ::model::DenseModel;
467 using ::model::PartialModelSpec;
468 
478 TEST_CASE("partial model writer verifier") {
479  SaveOption options;
481  std::filesystem::create_directory("test");
482  PartialModelSaver pms("test/pms-test", options);
483 
484  auto first_part = std::make_shared<DenseModel>(4, PartialModelSpec{label_id_t{1}, 4, 20});
485  // this just causes the setup, we can't create any contradictions yet
486  REQUIRE_NOTHROW(pms.add_model(first_part));
487 
488  SUBCASE("mismatched features") {
489  PartialModelSpec spec{label_id_t{5}, 2, 20};
490  auto mismatched_features = std::make_shared<DenseModel>(7, spec);
491  auto valid = std::make_shared<DenseModel>(4, spec);
492  REQUIRE_THROWS(pms.add_model(mismatched_features));
493  CHECK_NOTHROW(pms.add_model(valid));
494  }
495 
496  SUBCASE("mismatched total labels") {
497  auto mismatched_labels = std::make_shared<DenseModel>(4, PartialModelSpec{label_id_t{5}, 2, 50});
498  auto valid = std::make_shared<DenseModel>(4, PartialModelSpec{label_id_t{5}, 2, 20});
499  REQUIRE_THROWS(pms.add_model(mismatched_labels));
500  CHECK_NOTHROW(pms.add_model(valid));
501  }
502 
503  SUBCASE("incomplete model") {
504  CHECK_THROWS(pms.finalize());
505  }
506 }
507 
508 TEST_CASE("label lower bound") {
509  struct TestModel : public PartialModelIO {
512  TestModel() {
513  m_SubFiles.push_back(WeightFileEntry{label_id_t{20}, 30, "", WeightFormat::DENSE_TXT});
514  m_SubFiles.push_back(WeightFileEntry{label_id_t{100}, 50, "", WeightFormat::DENSE_TXT});
515  }
516  };
517 
518  TestModel tm{};
519  CHECK(tm.label_lower_bound(label_id_t{0}) == tm.m_SubFiles.begin());
520  CHECK(tm.label_lower_bound(label_id_t{20}) == tm.m_SubFiles.begin());
521  CHECK(tm.label_lower_bound(label_id_t{40}) == tm.m_SubFiles.begin() + 1);
522  CHECK(tm.label_lower_bound(label_id_t{50}) == tm.m_SubFiles.begin() + 1);
523  CHECK(tm.label_lower_bound(label_id_t{80}) == tm.m_SubFiles.begin() + 1);
524  CHECK(tm.label_lower_bound(label_id_t{100}) == tm.m_SubFiles.begin() + 1);
525  CHECK(tm.label_lower_bound(label_id_t{120}) == tm.m_SubFiles.end());
526  CHECK(tm.label_lower_bound(label_id_t{200}) == tm.m_SubFiles.end());
527 }
528 
529 TEST_CASE("insert_sub_file") {
530  struct TestModel : public PartialModelIO {
531  TestModel() = default;
532  ~TestModel() = default;
533  void insert(long first, long count) {
534  insert_sub_file(WeightFileEntry{label_id_t{first}, count, "", WeightFormat::NULL_FORMAT});
535  }
536  };
537  TestModel pms;
538 
539  // this just causes the setup, we can't create any contradictions yet
540  REQUIRE_NOTHROW(pms.insert(1, 4));
541 
542 
543  SUBCASE("overlap predecessor") {
544  REQUIRE_THROWS(pms.insert(4, 2));
545  CHECK_NOTHROW(pms.insert(5, 2));
546  }
547 
548  SUBCASE("overlap successor") {
549  REQUIRE_THROWS(pms.insert(0, 2));
550  CHECK_NOTHROW(pms.insert(0, 1));
551  }
552 }
This class is used as an implementation detail to capture the common code of PartialModelSaver and Pa...
Definition: model-io.h:151
weight_file_iter_t label_lower_bound(label_id_t pos) const
Gets an iterator into the weight-file list that points to the first element whose starting label is l...
Definition: model-io.cpp:116
void read_metadata_file(const path &meta_file)
Definition: model-io.cpp:93
void insert_sub_file(const WeightFileEntry &data)
Inserts a new sub-file entry into the metadata object.
Definition: model-io.cpp:123
std::vector< WeightFileEntry > m_SubFiles
Definition: model-io.h:176
long num_labels() const noexcept
Gets the total number of labels.
Definition: model-io.h:158
long num_features() const noexcept
Gets the total number of features.
Definition: model-io.h:165
This class allows loading only a subset of the weights of a large model.
Definition: model-io.h:317
std::shared_ptr< Model > load_model(label_id_t label_begin, label_id_t label_end) const
Loads part of the model.
Definition: model-io.cpp:373
long num_weight_files() const
Returns the number of availabel weight files.
Definition: model-io.cpp:395
bool validate() const
Validates that all weight files exist.
Definition: model-io.cpp:441
const path & meta_file_path() const
The path to the metadata file.
Definition: model-io.h:338
SubModelRangeSpec get_loading_range(label_id_t label_begin, label_id_t label_end) const
Definition: model-io.cpp:346
PartialModelLoader(path meta_file, ESparseMode mode=DEFAULT)
Create a new PartialModelLoader for the given metadata file.
Definition: model-io.cpp:341
Manage saving a model consisting of multiple partial models.
Definition: model-io.h:236
void finalize()
Checks that all weights have been written and updates the metadata file.
Definition: model-io.cpp:277
PartialModelSaver(path target_file, SaveOption options, bool load_partial=false)
Create a new PartialModelSaver.
Definition: model-io.cpp:160
bool any_weight_vector_for_interval(label_id_t begin, label_id_t end) const
Checks if there are any weight vectors for the given interval.
Definition: model-io.cpp:255
std::pair< label_id_t, label_id_t > get_missing_weights() const
Get an interval labels for which weights are missing.
Definition: model-io.cpp:292
std::future< WeightFileEntry > add_model(const std::shared_ptr< const Model > &model, const std::optional< std::string > &file_path={})
Adds the weights of a partial model asynchronously.
Definition: model-io.cpp:172
void insert_sub_file(const WeightFileEntry &data)
Inserts a new sub-file entry into the metadata object.
Definition: model-io.cpp:123
void update_meta_file()
Updates the metadata file.
Definition: model-io.cpp:234
Strong typedef for an int to signify a label id.
Definition: types.h:20
A model combines a set of weight with some meta-information about these weights.
Definition: model.h:63
constexpr T to_index() const
! Explicitly convert to an integer.
Definition: opaque_int.h:32
building blocks for io procedures that are used by multiple io subsystems
#define THROW_ERROR(...)
Definition: common.h:23
nlohmann::json json
Definition: model-io.cpp:22
TEST_CASE("partial model writer verifier")
Definition: model-io.cpp:478
void save_weights_dispatch(std::ostream &target, const Model &model, SaveOption &options)
This function calls on of the save functions, depending on the format specified on options
Definition: model-io.cpp:43
void read_weights_dispatch(std::istream &source, WeightFormat format, Model &model)
This function calls the reads function that corresponds to the given weight format.
Definition: model-io.cpp:67
const std::array< const char *, 4 > weight_format_names
Translation from io::model::WeightFormat to std::string.
Definition: model-io.cpp:28
const std::array< bool, 4 > weight_format_sparsity
Lookup which mode has sparse weights.
Definition: model-io.cpp:33
std::shared_ptr< Model > make_model(long num_features, ::model::PartialModelSpec spec, bool sparse)
Definition: model-io.cpp:400
bool use_sparse_weights(PartialModelLoader::ESparseMode mode, WeightFormat format)
Definition: model-io.cpp:408
namespace for all model-related io functions.
Definition: model-io.h:92
void load_sparse_weights_txt(std::istream &source, Model &target)
Loads sparse weights from plain-text format.
Definition: weights.cpp:140
std::shared_ptr< Model > load_model(path source)
Definition: model-io.cpp:334
WeightFormat parse_weights_format(std::string_view name)
Gets the eighs.
Definition: model-io.cpp:84
void save_dense_weights_npy(std::streambuf &target, const Model &model)
Saves the dense weights in a npy file.
Definition: weights.cpp:73
void save_model(const path &target_file, const std::shared_ptr< const Model > &model, SaveOption options)
Saves a complete model to a file.
Definition: model-io.cpp:308
void save_as_sparse_weights_txt(std::ostream &target, const Model &model, double threshold)
Saves the weights in sparse plain-text format, culling small weights.
Definition: weights.cpp:111
WeightFormat
Describes the format in which the weight data has been saved.
Definition: model-io.h:99
@ DENSE_TXT
Dense Text Format
@ SPARSE_TXT
Sparse Text Format
@ DENSE_NPY
Dense Numpy Format
const char * to_string(WeightFormat format)
Definition: model-io.cpp:89
void load_dense_weights_txt(std::istream &source, Model &target)
Loads weights saved by io::model::save_dense_weights_txt.
Definition: weights.cpp:61
void save_dense_weights_txt(std::ostream &target, const Model &model)
Saves the dense weights in a plain-text format.
Definition: weights.cpp:51
void load_dense_weights_npy(std::streambuf &target, Model &model)
Loads dense weights from a npy file.
Definition: weights.cpp:82
void pin_to_data(const void *data)
Pint the calling thread to the NUMA node on which data resides.
Definition: numa.cpp:78
Main namespace in which all types, classes, and functions are defined.
Definition: app.h:15
WeightFormat Format
Format in which the weights will be saved.
Definition: model-io.h:115
double Culling
If saving in sparse mode, threshold below which weights will be omitted.
Definition: model-io.h:113
int Precision
Precision with which the labels will be saved.
Definition: model-io.h:112
int SplitFiles
Maximum number of weight vectors per file.
Definition: model-io.h:114
Collect the data about a weight file.
Definition: model-io.h:139
Specifies how to interpret a weight matrix for a partial model.
Definition: model.h:22