config.cpp 10.7 KB
Newer Older
1
2
3
4
/*!
 * Copyright (c) 2016 Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See LICENSE file in the project root for license information.
 */
Guolin Ke's avatar
Guolin Ke committed
5
6
7
8
#include <LightGBM/config.h>

#include <LightGBM/utils/common.h>
#include <LightGBM/utils/log.h>
9
#include <LightGBM/utils/random.h>
Guolin Ke's avatar
Guolin Ke committed
10

Guolin Ke's avatar
Guolin Ke committed
11
#include <limits>
Guolin Ke's avatar
Guolin Ke committed
12
13
14

namespace LightGBM {

Guolin Ke's avatar
Guolin Ke committed
15
void Config::KV2Map(std::unordered_map<std::string, std::string>& params, const char* kv) {
wxchan's avatar
wxchan committed
16
17
18
19
  std::vector<std::string> tmp_strs = Common::Split(kv, '=');
  if (tmp_strs.size() == 2) {
    std::string key = Common::RemoveQuotationSymbol(Common::Trim(tmp_strs[0]));
    std::string value = Common::RemoveQuotationSymbol(Common::Trim(tmp_strs[1]));
20
21
22
    if (!Common::CheckASCII(key) || !Common::CheckASCII(value)) {
      Log::Fatal("Do not support non-ascii characters in config.");
    }
wxchan's avatar
wxchan committed
23
24
    if (key.size() > 0) {
      auto value_search = params.find(key);
25
      if (value_search == params.end()) {  // not set
wxchan's avatar
wxchan committed
26
27
        params.emplace(key, value);
      } else {
28
        Log::Warning("%s is set=%s, %s=%s will be ignored. Current value: %s=%s",
wxchan's avatar
wxchan committed
29
30
31
32
33
34
35
36
37
          key.c_str(), value_search->second.c_str(), key.c_str(), value.c_str(),
          key.c_str(), value_search->second.c_str());
      }
    }
  } else {
    Log::Warning("Unknown parameter %s", kv);
  }
}

Guolin Ke's avatar
Guolin Ke committed
38
std::unordered_map<std::string, std::string> Config::Str2Map(const char* parameters) {
39
  std::unordered_map<std::string, std::string> params;
40
  auto args = Common::Split(parameters, " \t\n\r");
41
  for (auto arg : args) {
wxchan's avatar
wxchan committed
42
    KV2Map(params, Common::Trim(arg).c_str());
43
44
  }
  ParameterAlias::KeyAliasTransform(&params);
45
  return params;
46
47
}

Guolin Ke's avatar
Guolin Ke committed
48
void GetBoostingType(const std::unordered_map<std::string, std::string>& params, std::string* boosting) {
Guolin Ke's avatar
Guolin Ke committed
49
  std::string value;
Guolin Ke's avatar
Guolin Ke committed
50
  if (Config::GetString(params, "boosting", &value)) {
Guolin Ke's avatar
Guolin Ke committed
51
    std::transform(value.begin(), value.end(), value.begin(), Common::tolower);
Guolin Ke's avatar
Guolin Ke committed
52
    if (value == std::string("gbdt") || value == std::string("gbrt")) {
Guolin Ke's avatar
Guolin Ke committed
53
      *boosting = "gbdt";
54
    } else if (value == std::string("dart")) {
Guolin Ke's avatar
Guolin Ke committed
55
      *boosting = "dart";
Guolin Ke's avatar
Guolin Ke committed
56
    } else if (value == std::string("goss")) {
Guolin Ke's avatar
Guolin Ke committed
57
      *boosting = "goss";
58
    } else if (value == std::string("rf") || value == std::string("random_forest")) {
Guolin Ke's avatar
Guolin Ke committed
59
      *boosting = "rf";
Guolin Ke's avatar
Guolin Ke committed
60
    } else {
61
      Log::Fatal("Unknown boosting type %s", value.c_str());
Guolin Ke's avatar
Guolin Ke committed
62
63
64
65
    }
  }
}

Guolin Ke's avatar
Guolin Ke committed
66
void GetObjectiveType(const std::unordered_map<std::string, std::string>& params, std::string* objective) {
Guolin Ke's avatar
Guolin Ke committed
67
  std::string value;
Guolin Ke's avatar
Guolin Ke committed
68
  if (Config::GetString(params, "objective", &value)) {
Guolin Ke's avatar
Guolin Ke committed
69
    std::transform(value.begin(), value.end(), value.begin(), Common::tolower);
Guolin Ke's avatar
Guolin Ke committed
70
    *objective = value;
Guolin Ke's avatar
Guolin Ke committed
71
72
73
  }
}

Guolin Ke's avatar
Guolin Ke committed
74
void GetMetricType(const std::unordered_map<std::string, std::string>& params, std::vector<std::string>* metric) {
Guolin Ke's avatar
Guolin Ke committed
75
  std::string value;
Guolin Ke's avatar
Guolin Ke committed
76
  if (Config::GetString(params, "metric", &value)) {
Guolin Ke's avatar
Guolin Ke committed
77
    // clear old metrics
Guolin Ke's avatar
Guolin Ke committed
78
    metric->clear();
Guolin Ke's avatar
Guolin Ke committed
79
    // to lower
Guolin Ke's avatar
Guolin Ke committed
80
    std::transform(value.begin(), value.end(), value.begin(), Common::tolower);
Guolin Ke's avatar
Guolin Ke committed
81
82
    // split
    std::vector<std::string> metrics = Common::Split(value.c_str(), ',');
83
    // remove duplicate
Guolin Ke's avatar
Guolin Ke committed
84
    std::unordered_set<std::string> metric_sets;
Guolin Ke's avatar
Guolin Ke committed
85
86
87
88
    for (auto& met : metrics) {
      std::transform(met.begin(), met.end(), met.begin(), Common::tolower);
      if (metric_sets.count(met) <= 0) {
        metric_sets.insert(met);
Guolin Ke's avatar
Guolin Ke committed
89
90
      }
    }
Guolin Ke's avatar
Guolin Ke committed
91
92
    for (auto& met : metric_sets) {
      metric->push_back(met);
Guolin Ke's avatar
Guolin Ke committed
93
    }
Guolin Ke's avatar
Guolin Ke committed
94
    metric->shrink_to_fit();
Guolin Ke's avatar
Guolin Ke committed
95
  }
96
  // add names of objective function if not providing metric
Guolin Ke's avatar
Guolin Ke committed
97
98
  if (metric->empty() && value.size() == 0) {
    if (Config::GetString(params, "objective", &value)) {
99
      std::transform(value.begin(), value.end(), value.begin(), Common::tolower);
Guolin Ke's avatar
Guolin Ke committed
100
      metric->push_back(value);
101
102
    }
  }
Guolin Ke's avatar
Guolin Ke committed
103
104
}

Guolin Ke's avatar
Guolin Ke committed
105
void GetTaskType(const std::unordered_map<std::string, std::string>& params, TaskType* task) {
Guolin Ke's avatar
Guolin Ke committed
106
  std::string value;
Guolin Ke's avatar
Guolin Ke committed
107
  if (Config::GetString(params, "task", &value)) {
Guolin Ke's avatar
Guolin Ke committed
108
    std::transform(value.begin(), value.end(), value.begin(), Common::tolower);
Guolin Ke's avatar
Guolin Ke committed
109
    if (value == std::string("train") || value == std::string("training")) {
Guolin Ke's avatar
Guolin Ke committed
110
      *task = TaskType::kTrain;
Guolin Ke's avatar
Guolin Ke committed
111
    } else if (value == std::string("predict") || value == std::string("prediction")
Guolin Ke's avatar
Guolin Ke committed
112
               || value == std::string("test")) {
Guolin Ke's avatar
Guolin Ke committed
113
      *task = TaskType::kPredict;
114
    } else if (value == std::string("convert_model")) {
Guolin Ke's avatar
Guolin Ke committed
115
      *task = TaskType::kConvertModel;
116
    } else if (value == std::string("refit") || value == std::string("refit_tree")) {
Guolin Ke's avatar
Guolin Ke committed
117
      *task = TaskType::KRefitTree;
Guolin Ke's avatar
Guolin Ke committed
118
    } else {
119
      Log::Fatal("Unknown task type %s", value.c_str());
Guolin Ke's avatar
Guolin Ke committed
120
121
122
123
    }
  }
}

wxchan's avatar
wxchan committed
124
void GetDeviceType(const std::unordered_map<std::string, std::string>& params, std::string* device_type) {
Guolin Ke's avatar
Guolin Ke committed
125
  std::string value;
126
  if (Config::GetString(params, "device_type", &value)) {
Guolin Ke's avatar
Guolin Ke committed
127
128
    std::transform(value.begin(), value.end(), value.begin(), Common::tolower);
    if (value == std::string("cpu")) {
wxchan's avatar
wxchan committed
129
      *device_type = "cpu";
Guolin Ke's avatar
Guolin Ke committed
130
    } else if (value == std::string("gpu")) {
wxchan's avatar
wxchan committed
131
      *device_type = "gpu";
Guolin Ke's avatar
Guolin Ke committed
132
133
134
135
136
137
    } else {
      Log::Fatal("Unknown device type %s", value.c_str());
    }
  }
}

Guolin Ke's avatar
Guolin Ke committed
138
void GetTreeLearnerType(const std::unordered_map<std::string, std::string>& params, std::string* tree_learner) {
Guolin Ke's avatar
Guolin Ke committed
139
  std::string value;
Guolin Ke's avatar
Guolin Ke committed
140
  if (Config::GetString(params, "tree_learner", &value)) {
Guolin Ke's avatar
Guolin Ke committed
141
142
    std::transform(value.begin(), value.end(), value.begin(), Common::tolower);
    if (value == std::string("serial")) {
Guolin Ke's avatar
Guolin Ke committed
143
      *tree_learner = "serial";
Guolin Ke's avatar
Guolin Ke committed
144
    } else if (value == std::string("feature") || value == std::string("feature_parallel")) {
Guolin Ke's avatar
Guolin Ke committed
145
      *tree_learner = "feature";
Guolin Ke's avatar
Guolin Ke committed
146
    } else if (value == std::string("data") || value == std::string("data_parallel")) {
Guolin Ke's avatar
Guolin Ke committed
147
      *tree_learner = "data";
Guolin Ke's avatar
Guolin Ke committed
148
    } else if (value == std::string("voting") || value == std::string("voting_parallel")) {
Guolin Ke's avatar
Guolin Ke committed
149
      *tree_learner = "voting";
Guolin Ke's avatar
Guolin Ke committed
150
151
152
153
154
155
    } else {
      Log::Fatal("Unknown tree learner type %s", value.c_str());
    }
  }
}

Guolin Ke's avatar
Guolin Ke committed
156
void Config::Set(const std::unordered_map<std::string, std::string>& params) {
Guolin Ke's avatar
Guolin Ke committed
157
158
159
160
  // generate seeds by seed.
  if (GetInt(params, "seed", &seed)) {
    Random rand(seed);
    int int_max = std::numeric_limits<short>::max();
Guolin Ke's avatar
Guolin Ke committed
161
162
163
164
    data_random_seed = static_cast<int>(rand.NextShort(0, int_max));
    bagging_seed = static_cast<int>(rand.NextShort(0, int_max));
    drop_seed = static_cast<int>(rand.NextShort(0, int_max));
    feature_fraction_seed = static_cast<int>(rand.NextShort(0, int_max));
Guolin Ke's avatar
Guolin Ke committed
165
166
  }

Guolin Ke's avatar
Guolin Ke committed
167
168
169
170
171
172
  GetTaskType(params, &task);
  GetBoostingType(params, &boosting);
  GetMetricType(params, &metric);
  GetObjectiveType(params, &objective);
  GetDeviceType(params, &device_type);
  GetTreeLearnerType(params, &tree_learner);
Guolin Ke's avatar
Guolin Ke committed
173

Guolin Ke's avatar
Guolin Ke committed
174
  GetMembersFromString(params);
175

Guolin Ke's avatar
Guolin Ke committed
176
177
  // sort eval_at
  std::sort(eval_at.begin(), eval_at.end());
Guolin Ke's avatar
Guolin Ke committed
178

Guolin Ke's avatar
Guolin Ke committed
179
180
181
  if (valid_data_initscores.size() == 0 && valid.size() > 0) {
    valid_data_initscores = std::vector<std::string>(valid.size(), "");
  }
182
  CHECK(valid.size() == valid_data_initscores.size());
Guolin Ke's avatar
Guolin Ke committed
183
184
185

  // check for conflicts
  CheckParamConflict();
186

Guolin Ke's avatar
Guolin Ke committed
187
  if (verbosity == 1) {
Guolin Ke's avatar
Guolin Ke committed
188
    LightGBM::Log::ResetLogLevel(LightGBM::LogLevel::Info);
Guolin Ke's avatar
Guolin Ke committed
189
  } else if (verbosity == 0) {
Guolin Ke's avatar
Guolin Ke committed
190
    LightGBM::Log::ResetLogLevel(LightGBM::LogLevel::Warning);
Guolin Ke's avatar
Guolin Ke committed
191
  } else if (verbosity >= 2) {
Guolin Ke's avatar
Guolin Ke committed
192
193
194
195
196
197
    LightGBM::Log::ResetLogLevel(LightGBM::LogLevel::Debug);
  } else {
    LightGBM::Log::ResetLogLevel(LightGBM::LogLevel::Fatal);
  }
}

Guolin Ke's avatar
Guolin Ke committed
198
199
200
201
202
203
204
bool CheckMultiClassObjective(const std::string& objective) {
  return (objective == std::string("multiclass")
          || objective == std::string("multiclassova")
          || objective == std::string("softmax")
          || objective == std::string("multiclass_ova")
          || objective == std::string("ova")
          || objective == std::string("ovr"));
205
206
}

Guolin Ke's avatar
Guolin Ke committed
207
208
209
void Config::CheckParamConflict() {
  // check if objective, metric, and num_class match
  int num_class_check = num_class;
210
  bool objective_custom = objective == std::string("none") || objective == std::string("null")
211
                                       || objective == std::string("custom") || objective == std::string("na");
Guolin Ke's avatar
Guolin Ke committed
212
  bool objective_type_multiclass = CheckMultiClassObjective(objective) || (objective_custom && num_class_check > 1);
213

214
  if (objective_type_multiclass) {
Guolin Ke's avatar
Guolin Ke committed
215
216
    if (num_class_check <= 1) {
      Log::Fatal("Number of classes should be specified and greater than 1 for multiclass training");
217
218
    }
  } else {
Guolin Ke's avatar
Guolin Ke committed
219
    if (task == TaskType::kTrain && num_class_check != 1) {
220
221
      Log::Fatal("Number of classes must be 1 for non-multiclass training");
    }
222
  }
Guolin Ke's avatar
Guolin Ke committed
223
  for (std::string metric_type : metric) {
224
    bool metric_custom_or_none = metric_type == std::string("none") || metric_type == std::string("null")
225
226
227
228
229
                                 || metric_type == std::string("custom") || metric_type == std::string("na");
    bool metric_type_multiclass = (CheckMultiClassObjective(metric_type)
                                   || metric_type == std::string("multi_logloss")
                                   || metric_type == std::string("multi_error")
                                   || (metric_custom_or_none && num_class_check > 1));
Guolin Ke's avatar
Guolin Ke committed
230
    if ((objective_type_multiclass && !metric_type_multiclass)
231
232
        || (!objective_type_multiclass && metric_type_multiclass)) {
      Log::Fatal("Multiclass objective and metrics don't match");
233
    }
234
  }
235

Guolin Ke's avatar
Guolin Ke committed
236
  if (num_machines > 1) {
Guolin Ke's avatar
Guolin Ke committed
237
238
239
    is_parallel = true;
  } else {
    is_parallel = false;
Guolin Ke's avatar
Guolin Ke committed
240
    tree_learner = "serial";
Guolin Ke's avatar
Guolin Ke committed
241
242
  }

Guolin Ke's avatar
Guolin Ke committed
243
  bool is_single_tree_learner = tree_learner == std::string("serial");
Guolin Ke's avatar
Guolin Ke committed
244
245

  if (is_single_tree_learner) {
Guolin Ke's avatar
Guolin Ke committed
246
    is_parallel = false;
Guolin Ke's avatar
Guolin Ke committed
247
    num_machines = 1;
Guolin Ke's avatar
Guolin Ke committed
248
249
  }

Guolin Ke's avatar
Guolin Ke committed
250
  if (is_single_tree_learner || tree_learner == std::string("feature")) {
Guolin Ke's avatar
Guolin Ke committed
251
    is_parallel_find_bin = false;
Guolin Ke's avatar
Guolin Ke committed
252
253
  } else if (tree_learner == std::string("data")
             || tree_learner == std::string("voting")) {
Guolin Ke's avatar
Guolin Ke committed
254
    is_parallel_find_bin = true;
Guolin Ke's avatar
Guolin Ke committed
255
256
    if (histogram_pool_size >= 0
        && tree_learner == std::string("data")) {
257
258
      Log::Warning("Histogram LRU queue was enabled (histogram_pool_size=%f).\n"
                   "Will disable this to reduce communication costs",
Guolin Ke's avatar
Guolin Ke committed
259
                   histogram_pool_size);
tks's avatar
tks committed
260
      // Change pool size to -1 (no limit) when using data parallel to reduce communication costs
Guolin Ke's avatar
Guolin Ke committed
261
      histogram_pool_size = -1;
262
    }
Guolin Ke's avatar
Guolin Ke committed
263
  }
264
  // Check max_depth and num_leaves
Guolin Ke's avatar
Guolin Ke committed
265
266
  if (max_depth > 0) {
    int full_num_leaves = static_cast<int>(std::pow(2, max_depth));
267
    if (full_num_leaves > num_leaves
Guolin Ke's avatar
Guolin Ke committed
268
        && num_leaves == kDefaultNumLeaves) {
269
      Log::Warning("Accuracy may be bad since you didn't set num_leaves and 2^max_depth > num_leaves");
270
    }
271
    num_leaves = std::min(num_leaves, 2 << max_depth);
272
  }
Guolin Ke's avatar
Guolin Ke committed
273
274
}

Guolin Ke's avatar
Guolin Ke committed
275
276
277
278
279
280
281
282
283
std::string Config::ToString() const {
  std::stringstream str_buf;
  str_buf << "[boosting: " << boosting << "]\n";
  str_buf << "[objective: " << objective << "]\n";
  str_buf << "[metric: " << Common::Join(metric, ",") << "]\n";
  str_buf << "[tree_learner: " << tree_learner << "]\n";
  str_buf << "[device_type: " << device_type << "]\n";
  str_buf << SaveMembersToString();
  return str_buf.str();
Guolin Ke's avatar
Guolin Ke committed
284
285
286
}

}  // namespace LightGBM