nnet-optimize-test.cc
Go to the documentation of this file.
1 // nnet3/nnet-optimize-test.cc
2 
3 // Copyright 2015 Johns Hopkins University (author: Daniel Povey)
4 
5 // See ../../COPYING for clarification regarding multiple authors
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 // http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
15 // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
16 // MERCHANTABLITY OR NON-INFRINGEMENT.
17 // See the Apache 2 License for the specific language governing permissions and
18 // limitations under the License.
19 
20 #include "nnet3/nnet-nnet.h"
21 #include "nnet3/nnet-compile.h"
22 #include "nnet3/nnet-analyze.h"
23 #include "nnet3/nnet-test-utils.h"
24 #include "nnet3/nnet-optimize.h"
25 #include "nnet3/nnet-compute.h"
26 
27 namespace kaldi {
28 namespace nnet3 {
29 
30 // Run the test without optimizations and with optimizations specified by the
31 // configs (the optimized version is done with class CachingOptimizingCompiler).
32 // Only print warnings; we'll fail the whole test later.
33 static bool UnitTestNnetOptimizeWithOptions(int32 srand_seed,
34  NnetOptimizeOptions opt_config,
35  CachingOptimizingCompilerOptions compiler_config) {
36 
37  //opt_config.convert_addition = false;
38  //opt_config.remove_assignments = false;
39  //opt_config.move_sizing_commands = false;
40  //opt_config.allocate_from_other = false;
41 
42  srand(srand_seed); // so that we can compare between differnt optimization types
43  // with the randomly generated network staying the same.
44 
45  struct NnetGenerationOptions gen_config;
46 
47  std::vector<std::string> configs;
48  GenerateConfigSequence(gen_config, &configs);
49  Nnet nnet;
50  for (size_t j = 0; j < configs.size(); j++) {
51  KALDI_LOG << "Input config[" << j << "] is: " << configs[j];
52  std::istringstream is(configs[j]);
53  nnet.ReadConfig(is);
54  }
55 
56  ComputationRequest request;
57  std::vector<Matrix<BaseFloat> > inputs;
58  ComputeExampleComputationRequestSimple(nnet, &request, &inputs);
59 
60  NnetComputation computation;
61  Compiler compiler(request, nnet);
62 
63  CompilerOptions opts;
64  compiler.CreateComputation(opts, &computation);
65  {
66  std::ostringstream os;
67  computation.Print(os, nnet);
68  KALDI_LOG << "Generated computation with no optimization or shortcut is: " << os.str();
69  }
70  CheckComputationOptions check_config;
71  // we can do the rewrite check since it's before optimization.
72  check_config.check_rewrite = true;
73  ComputationChecker checker(check_config, nnet, computation);
74  checker.Check();
75 
76  CachingOptimizingCompiler opt_compiler(nnet, opt_config, compiler_config);
77 
78  const NnetComputation &computation_opt = *opt_compiler.Compile(request);
79 
80  {
81  std::ostringstream os;
82  computation_opt.Print(os, nnet);
83  KALDI_LOG << "Optimized computation is: " << os.str();
84  }
85 
86  NnetComputeOptions compute_opts;
87  if (RandInt(0, 1) == 0)
88  compute_opts.debug = true;
89 
90  computation.ComputeCudaIndexes();
91  // computation_opt has already had this function called.
92 
93  Nnet nnet_to_update(nnet); // copy of the nnet that we update... needed to
94  // test the consolidation of backprop commands,
95  // otherwise the optimized and non-optimized
96  // comptuations differ.
97  ScaleNnet(0.0, &nnet_to_update);
98  // with natural gradient, the consolidation would affect the final model
99  // params -> test just the gradient.
100  SetNnetAsGradient(&nnet_to_update);
101 
102  NnetComputer computer(compute_opts,
103  computation,
104  nnet,
105  &nnet_to_update);
106 
107  Nnet nnet_opt(nnet); // copy of the nnet for the optimized computation.
108  // necessary in case backprop changes parameters.
109  Nnet nnet_opt_to_update(nnet_opt);
110  ScaleNnet(0.0, &nnet_opt_to_update);
111  SetNnetAsGradient(&nnet_opt_to_update);
112 
113  // NnetComputer for the optimized version of the computation.
114  NnetComputer computer_opt(compute_opts,
115  computation_opt,
116  nnet_opt,
117  &nnet_opt_to_update);
118 
119  // provide the input to the computations.
120  for (size_t i = 0; i < request.inputs.size(); i++) {
121  CuMatrix<BaseFloat> temp(inputs[i]);
122  KALDI_LOG << "Input sum is " << temp.Sum();
123  computer.AcceptInput(request.inputs[i].name, &temp);
124  CuMatrix<BaseFloat> temp2(inputs[i]);
125  computer_opt.AcceptInput(request.inputs[i].name, &temp2);
126  }
127 
128 
129 
130 
131  KALDI_LOG << "Running non-optimized forward computation";
132  srand(srand_seed);
133  ResetGenerators(&nnet);
134  computer.Run();
135  KALDI_LOG << "Running optimized forward computation";
136  srand(srand_seed);
137  ResetGenerators(&nnet_opt);
138  computer_opt.Run();
139 
140  const CuMatrixBase<BaseFloat> &output(computer.GetOutput("output"));
141  KALDI_LOG << "Output sum (not optimized) is " << output.Sum();
142  const CuMatrixBase<BaseFloat> &output_opt(computer_opt.GetOutput("output"));
143  KALDI_LOG << "Output sum (optimized) is " << output_opt.Sum();
144  if (!ApproxEqual(output, output_opt)) {
145  KALDI_WARN << "Non-optimized and optimized versions of the computation give "
146  << "different outputs: " << output << " vs. " << output_opt;
147  return false;
148  }
149 
150  CuMatrix<BaseFloat> output_deriv(output.NumRows(), output.NumCols());
151  output_deriv.SetRandn();
152  CuMatrix<BaseFloat> output_deriv_opt(output_deriv);
153 
154  if (request.outputs[0].has_deriv) {
155  computer.AcceptInput("output", &output_deriv);
156  computer_opt.AcceptInput("output", &output_deriv_opt);
157 
158  KALDI_LOG << "Running non-optimized backward computation";
159  computer.Run();
160  KALDI_LOG << "Running optimized backward computation";
161  computer_opt.Run();
162  for (size_t i = 0; i < request.inputs.size(); i++) {
163  if (request.inputs[i].has_deriv) {
164  const CuMatrixBase<BaseFloat> &in_deriv =
165  computer.GetOutput(request.inputs[i].name);
166  const CuMatrixBase<BaseFloat> &in_deriv_opt =
167  computer_opt.GetOutput(request.inputs[i].name);
168  KALDI_LOG << "Input-deriv sum for input '" << request.inputs[i].name
169  << "' (non-optimized) is " << in_deriv.Sum();
170  KALDI_LOG << "Input-deriv sum for input '" << request.inputs[i].name
171  << "' (optimized) is " << in_deriv_opt.Sum();
172  if (!ApproxEqual(in_deriv, in_deriv_opt)) {
173  KALDI_WARN << "Non-optimized and optimized versions of the "
174  << "computation give different input-derivs.";
175  return false;
176  }
177  }
178  }
179  }
180 
181  if (!NnetParametersAreIdentical(nnet_to_update,
182  nnet_opt_to_update, 1.0e-05)) {
183  KALDI_WARN << "Neural networks differ after training, between "
184  << "optimized and non-optimized computation.";
185  return false;
186  } else {
187  return true;
188  }
189 }
190 
191 
192 // This test runs the computation with and without optimization, and checks that
193 // the outputs are the same.
194 static void UnitTestNnetOptimizeInternal(int32 srand_seed) {
195  NnetOptimizeOptions optimize_all;
197 
198  // randomly sometimes set min_deriv and max_deriv to small/large values,
199  // which will cause some of the LimitDerivativeTimes() code to be called
200  // (without really changing anything).
201  if (RandInt(0, 3) == 0) optimize_all.min_deriv_time = -200;
202  if (RandInt(0, 3) == 0) optimize_all.max_deriv_time = 1000;
203 
204  // this is useful for debugging as it removes nans:
205  // optimize_all.initialize_undefined = false;
206  bool success = UnitTestNnetOptimizeWithOptions(srand_seed, optimize_all,
207  compiler_all);
208  if (success)
209  return;
210 
211  // Test failed with full optimization. Slowly retry with various
212  // optimizations switched off.
213  NnetOptimizeOptions optimize = optimize_all;
214  CachingOptimizingCompilerOptions compiler = compiler_all;
215 
216 
217  compiler.use_shortcut = false;
218  bool succ_no_shortcut = UnitTestNnetOptimizeWithOptions(srand_seed, optimize,
219  compiler);
220  compiler = compiler_all;
221 
222 
223  optimize.propagate_in_place = false;
224  bool succ_no_propagate_in_place = UnitTestNnetOptimizeWithOptions(srand_seed, optimize,
225  compiler);
226  optimize = optimize_all;
227 
228  optimize.backprop_in_place = false;
229  bool succ_no_backprop_in_place = UnitTestNnetOptimizeWithOptions(srand_seed, optimize,
230  compiler);
231  optimize = optimize_all;
232 
233  optimize.optimize_row_ops = false;
234  bool succ_no_row_ops = UnitTestNnetOptimizeWithOptions(srand_seed, optimize,
235  compiler);
236  optimize = optimize_all;
237 
238  optimize.convert_addition = false;
239  bool succ_no_convert_addition = UnitTestNnetOptimizeWithOptions(srand_seed, optimize,
240  compiler);
241  optimize = optimize_all;
242 
243  optimize.remove_assignments = false;
244  bool succ_no_remove_assignments = UnitTestNnetOptimizeWithOptions(srand_seed, optimize,
245  compiler);
246  optimize = optimize_all;
247 
248  optimize.initialize_undefined = false;
249  bool succ_no_initialize_undefined = UnitTestNnetOptimizeWithOptions(srand_seed, optimize,
250  compiler);
251  optimize = optimize_all;
252 
253  optimize.allocate_from_other = false;
254  bool succ_no_allocate_from_other = UnitTestNnetOptimizeWithOptions(srand_seed, optimize,
255  compiler);
256  optimize = optimize_all;
257 
258  optimize.move_sizing_commands = false;
259  bool succ_no_move_sizing_commands = UnitTestNnetOptimizeWithOptions(srand_seed, optimize,
260  compiler);
261  optimize = optimize_all;
262 
263  optimize.snip_row_ops = false;
264  bool succ_no_snip_row_ops = UnitTestNnetOptimizeWithOptions(srand_seed, optimize,
265  compiler);
266  optimize = optimize_all;
267 
268 
269  optimize.min_deriv_time = std::numeric_limits<int32>::min();
270  optimize.max_deriv_time = std::numeric_limits<int32>::max();
271  optimize.max_deriv_time_relative = std::numeric_limits<int32>::max();
272  bool succ_no_deriv_time = UnitTestNnetOptimizeWithOptions(srand_seed, optimize,
273  compiler);
274  optimize = optimize_all;
275 
276 
277 #define KALDI_SUCCFAIL(b) ((b) ? "SUCCESS" : "FAILURE")
278  KALDI_ERR
279  << "Test failed with all optimizations enabled. Retried test with the "
280  << "following optimizations turned off:"
281  << "\n use_shortcut ... " << KALDI_SUCCFAIL(succ_no_shortcut)
282  << "\n propagate_in_place ... " << KALDI_SUCCFAIL(succ_no_propagate_in_place)
283  << "\n backprop_in_place ... " << KALDI_SUCCFAIL(succ_no_backprop_in_place)
284  << "\n optimize_row_ops ... " << KALDI_SUCCFAIL(succ_no_row_ops)
285  << "\n convert_addition ... " << KALDI_SUCCFAIL(succ_no_convert_addition)
286  << "\n remove_assignments ... " << KALDI_SUCCFAIL(succ_no_remove_assignments)
287  << "\n initialize_undefined ... " << KALDI_SUCCFAIL(succ_no_initialize_undefined)
288  << "\n allocate_from_other ... " << KALDI_SUCCFAIL(succ_no_allocate_from_other)
289  << "\n move_sizing_commands ... " << KALDI_SUCCFAIL(succ_no_move_sizing_commands)
290  << "\n snip_row_ops ... " << KALDI_SUCCFAIL(succ_no_snip_row_ops)
291  << "\n no_deriv_time ... " << KALDI_SUCCFAIL(succ_no_deriv_time);
292 #undef KALDI_SUCCFAIL
293 }
294 
295 static void UnitTestNnetOptimize() {
296  for (int32 srand_seed = 0; srand_seed < 40; srand_seed++) {
297  KALDI_LOG << "About to run UnitTestNnetOptimizeInternal with srand_seed = "
298  << srand_seed;
299  UnitTestNnetOptimizeInternal(srand_seed);
300  }
301 }
302 
303 
304 
305 } // namespace nnet3
306 } // namespace kaldi
307 
308 int main() {
309  using namespace kaldi;
310  using namespace kaldi::nnet3;
311  SetVerboseLevel(3);
312 
313 #if HAVE_CUDA == 1
314  CuDevice::Instantiate().SetDebugStrideMode(true);
315  CuDevice::Instantiate().SelectGpuId("no");
317  CuDevice::Instantiate().SelectGpuId("yes");
318 #endif
320 
321  KALDI_LOG << "Nnet tests succeeded.";
322 
323  return 0;
324 }
static void UnitTestNnetOptimizeInternal(int32 srand_seed)
This code computes Goodness of Pronunciation (GOP) and extracts phone-level pronunciation feature for...
Definition: chain.dox:20
void ScaleNnet(BaseFloat scale, Nnet *nnet)
Scales the nnet parameters and stats by this scale.
Definition: nnet-utils.cc:312
void ReadConfig(std::istream &config_file)
Definition: nnet-nnet.cc:189
bool NnetParametersAreIdentical(const Nnet &nnet1, const Nnet &nnet2, BaseFloat threshold=1.0e-05)
Used for testing that the updatable parameters in two networks are the same.
#define KALDI_SUCCFAIL(b)
This class enables you to do the compilation and optimization in one call, and also ensures that if t...
Real Sum() const
Definition: cu-matrix.cc:3012
This file contains various routines that are useful in test code.
void Print(std::ostream &os, const Nnet &nnet) const
This file contains utilities for analyzing and checking computations, which are used in the optimizat...
kaldi::int32 int32
std::vector< IoSpecification > inputs
This class represents a matrix that&#39;s stored on the GPU if we have one, and in memory if not...
Definition: matrix-common.h:71
static bool UnitTestNnetOptimizeWithOptions(int32 srand_seed, NnetOptimizeOptions opt_config, CachingOptimizingCompilerOptions compiler_config)
void SetNnetAsGradient(Nnet *nnet)
Sets nnet as gradient by Setting is_gradient_ to true and learning_rate_ to 1 for each UpdatableCompo...
Definition: nnet-utils.cc:292
void SetVerboseLevel(int32 i)
This should be rarely used, except by programs using Kaldi as library; command-line programs set the ...
Definition: kaldi-error.h:64
void AcceptInput(const std::string &node_name, CuMatrix< BaseFloat > *input)
e.g.
void ResetGenerators(Nnet *nnet)
This function calls &#39;ResetGenerator()&#39; on all components in &#39;nnet&#39; that inherit from class RandomComp...
Definition: nnet-utils.cc:582
void ComputeExampleComputationRequestSimple(const Nnet &nnet, ComputationRequest *request, std::vector< Matrix< BaseFloat > > *inputs)
This function computes an example computation request, for testing purposes.
const CuMatrixBase< BaseFloat > & GetOutput(const std::string &node_name)
#define KALDI_ERR
Definition: kaldi-error.h:147
static void UnitTestNnetOptimize()
#define KALDI_WARN
Definition: kaldi-error.h:150
std::shared_ptr< const NnetComputation > Compile(const ComputationRequest &request)
Does the compilation and returns a const pointer to the result, which is owned by this class...
Matrix for CUDA computing.
Definition: matrix-common.h:69
class NnetComputer is responsible for executing the computation described in the "computation" object...
Definition: nnet-compute.h:59
void CreateComputation(const CompilerOptions &opts, NnetComputation *computation)
Definition: nnet-compile.cc:50
std::vector< IoSpecification > outputs
This class creates an initial version of the NnetComputation, without any optimization or sharing of ...
Definition: nnet-compile.h:44
void GenerateConfigSequence(const NnetGenerationOptions &opts, std::vector< std::string > *configs)
Generates a sequence of at least one config files, output as strings, where the first in the sequence...
#define KALDI_LOG
Definition: kaldi-error.h:153
static bool ApproxEqual(float a, float b, float relative_tolerance=0.001)
return abs(a - b) <= relative_tolerance * (abs(a)+abs(b)).
Definition: kaldi-math.h:265
int32 RandInt(int32 min_val, int32 max_val, struct RandomState *state)
Definition: kaldi-math.cc:95
void Run()
This does either the forward or backward computation, depending when it is called (in a typical compu...
int main()