Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e97df65
dropout_schedule: Adding dropout schedule to scripts
vimalmanohar Dec 5, 2016
8d26ce0
dropout_schedule: Add set-dropout-proportion in nnet3 utils
vimalmanohar Dec 6, 2016
1424c57
Changing option
vimalmanohar Dec 7, 2016
818d495
dropout_schedule: Print dropout info
vimalmanohar Dec 7, 2016
3342dd8
dropout_schedule: Adding more comments and fixing bug
vimalmanohar Dec 7, 2016
f17b0fc
dropout_schedule: Bug fix
vimalmanohar Dec 8, 2016
5a6a9b1
dropout_schedule: Fixed bug
vimalmanohar Dec 8, 2016
4ece089
dropout_schedule: Fixing logging
vimalmanohar Dec 9, 2016
0dd66c1
dropout_schedule: Not printing shrinkage when its 1.0
vimalmanohar Dec 9, 2016
f6d25a2
Merging
vimalmanohar Dec 11, 2016
635bb6e
Merge branch 'master' of github.com:kaldi-asr/kaldi into dropout_sche…
vimalmanohar Dec 11, 2016
7109c43
change dropout_parser strategy
GaofengCheng Dec 12, 2016
5435f23
adding frame level dropout to TDNN+LSTM on AMI SDM1 #1248
GaofengCheng Dec 14, 2016
7899760
dropout_schedule: Add strict checking of dropout schedule
vimalmanohar Dec 14, 2016
18404a9
Merge branch 'dropout_schedule' into nnet3-dropout
vimalmanohar Dec 15, 2016
4371f7a
Merge pull request #6 from GaofengCheng/nnet3-dropout
vimalmanohar Dec 15, 2016
c86b3e4
dropout_schedule: Better way to fix the same data proportion in sched…
vimalmanohar Dec 15, 2016
bc72ed6
dropout_schedule: SetDropoutProportion to 0 in nnet-combine and nnet-…
vimalmanohar Dec 16, 2016
879e2e1
dropout_schedule: Adding back the function SetDropoutProportion that …
vimalmanohar Dec 16, 2016
18a5c58
dropout_schedule: Fixing deprecated dropout option
vimalmanohar Dec 16, 2016
d7ebc31
dropout_schedule: Sorting models to combine for easy reading of values
vimalmanohar Dec 16, 2016
8484c58
dropout_schedule: Merging from master
vimalmanohar Dec 28, 2016
a01ed13
dropout: Minor bug fix
vimalmanohar Jan 9, 2017
e6d886a
dropout_schedule: Simplying dropout in script
vimalmanohar Jan 19, 2017
4e8960b
dropout_schedule: Simplified dropout schedule functions
vimalmanohar Jan 21, 2017
a6b9389
dropout_schedule: removing example script
vimalmanohar Jan 21, 2017
df7e7b6
dropout_schedule: fixing minor errors
vimalmanohar Jan 21, 2017
c978be3
dropout_schedule: Made functions internal
vimalmanohar Jan 21, 2017
e9d498b
dropout_schedule: Added self test
vimalmanohar Jan 23, 2017
d8adee9
dropout_schedule: removing dropout option
vimalmanohar Jan 23, 2017
09cc27b
dropout_schedule: Add more examples
vimalmanohar Jan 23, 2017
2e94018
dropout_schedule: Made self_test to not run on import
vimalmanohar Jan 23, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions egs/wsj/s5/steps/libs/nnet3/train/chain_objf/acoustic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,9 @@ def train_one_iteration(dir, iter, srand, egs_dir,
leaky_hmm_coefficient,
momentum, max_param_change, shuffle_buffer_size,
frame_subsampling_factor, truncate_deriv_weights,
run_opts, background_process_handler=None):
run_opts,
dropout_edit_string="",
background_process_handler=None):
""" Called from steps/nnet3/chain/train.py for one iteration for
neural network training with LF-MMI objective

Expand All @@ -237,9 +239,10 @@ def train_one_iteration(dir, iter, srand, egs_dir,
if os.path.exists('{0}/srand'.format(dir)):
try:
saved_srand = int(open('{0}/srand'.format(dir)).readline().strip())
except (IOError, ValueError) as e:
raise Exception("Exception while reading the random seed "
"for training: {0}".format(e.str()))
except (IOError, ValueError):
logger.error("Exception while reading the random seed "
"for training")
raise
if srand != saved_srand:
logger.warning("The random seed provided to this iteration "
"(srand={0}) is different from the one saved last "
Expand Down Expand Up @@ -302,6 +305,17 @@ def train_one_iteration(dir, iter, srand, egs_dir,
cur_num_chunk_per_minibatch = num_chunk_per_minibatch / 2
cur_max_param_change = float(max_param_change) / math.sqrt(2)

raw_model_string = '{0} {1}'.format(raw_model_string, dropout_edit_string)

shrink_info_str = ''
if shrinkage_value != 1.0:
shrink_info_str = ' and shrink value is {0}'.format(shrinkage_value)

logger.info("On iteration {0}, learning rate is {1}"
"{shrink_info}.".format(
iter, learning_rate,
shrink_info=shrink_info_str))

train_new_models(dir=dir, iter=iter, srand=srand, num_jobs=num_jobs,
num_archives_processed=num_archives_processed,
num_archives=num_archives,
Expand Down Expand Up @@ -521,7 +535,7 @@ def combine_models(dir, num_iters, models_to_combine, num_chunk_per_minibatch,

models_to_combine.add(num_iters)

for iter in models_to_combine:
for iter in sorted(models_to_combine):
model_file = '{0}/{1}.mdl'.format(dir, iter)
if os.path.exists(model_file):
raw_model_strings.append(
Expand Down
29 changes: 28 additions & 1 deletion egs/wsj/s5/steps/libs/nnet3/train/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
import shutil

import libs.common as common_lib
import libs.nnet3.train.dropout_schedule as dropout_schedule
from dropout_schedule import *

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better if you just imported get_dropout_edit_string, because that's the only function we need from there, and if you just import the one function it's clear that that's the only one that's the real interface. You could rename all the others with underscores at the start of their names (assuming they really are internal to the module and assuming that's what the Google style guide recommends in such circumstances).


logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())


class RunOpts:
class RunOpts(object):
"""A structure to store run options.

Run options like queue.pl and run.pl, along with their memory
Expand Down Expand Up @@ -530,6 +532,31 @@ def __init__(self):
Note: we implemented it in such a way that it
doesn't increase the effective learning
rate.""")
self.parser.add_argument("--trainer.dropout-schedule", type=str,
action=common_lib.NullstrToNoneAction,
dest='dropout_schedule', default=None,
help="""Use this to specify the dropout
schedule. You specify a piecewise linear
function on the domain [0,1], where 0 is the
start and 1 is the end of training; the
function-argument (x) rises linearly with the
amount of data you have seen, not iteration
number (this improves invariance to
num-jobs-{initial-final}). E.g. '0,0.2,0'
means 0 at the start; 0.2 after seeing half
the data; and 0 at the end. You may specify
the x-value of selected points, e.g.
'0,0.2@0.25,0' means that the 0.2
dropout-proportion is reached a quarter of the
way through the data. The start/end x-values
are at x=0/x=1, and other unspecified x-values
are interpolated between known x-values. You
may specify different rules for different
component-name patterns using 'pattern1=func1
pattern2=func2', e.g. 'relu*=0,0.1,0
lstm*=0,0.2,0'. More general should precede
less general patterns, as they are applied
sequentially.""")

# General options
self.parser.add_argument("--stage", type=int, default=-4,
Expand Down
236 changes: 236 additions & 0 deletions egs/wsj/s5/steps/libs/nnet3/train/dropout_schedule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@


# Copyright 2016 Vimal Manohar
# Apache 2.0

"""This module contains methods related to scheduling dropout.
"""

import logging

logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())


def _parse_dropout_option(dropout_option):
"""Parses the string option to --trainer.dropout-schedule and
returns a list of dropout schedules for different component name patterns.
Calls _parse_dropout_string() function for each component name pattern
in the option.

Arguments:
dropout_option: The string option passed to --trainer.dropout-schedule.
See its help for details.
num_archive_to_process: See _parse_dropout_string() for details.

Returns a list of (component_name, dropout_schedule) tuples,
where dropout_schedule is itself a list of
(data_fraction, dropout_proportion) tuples.
A data fraction of 0 corresponds to beginning of training
and 1 corresponds to all data.
"""
components = dropout_option.strip().split(' ')
dropout_schedule = []
for component in components:
parts = component.split('=')

if len(parts) == 2:
component_name = parts[0]
this_dropout_str = parts[1]
elif len(parts) == 1:
component_name = '*'
this_dropout_str = parts[0]
else:
raise Exception("The dropout schedule must be specified in the "
"format 'pattern1=func1 patter2=func2' where "
"the pattern can be omitted for a global function "
"for all components.\n"
"Got {0} in {1}".format(component, dropout_option))

this_dropout_values = _parse_dropout_string(this_dropout_str)
dropout_schedule.append((component_name, this_dropout_values))

logger.info("Dropout schedules for component names is as follows:")
logger.info("<component-name-pattern>: [(num_archives_processed), "
"(dropout_proportion) ...]")
for name, schedule in dropout_schedule:
logger.info("{0}: {1}".format(name, schedule))

return dropout_schedule


def _parse_dropout_string(dropout_str):
"""Parses the dropout schedule from the string corresponding to a
single component in --trainer.dropout-schedule.
This is a module-internal function called by parse_dropout_function().

Arguments:
dropout_str: Specifies dropout schedule for a particular component
name pattern.
See help for the option --trainer.dropout-schedule.

Returns a list of (data_fraction_processed, dropout_proportion) tuples
sorted in descending order of num_archives_processed.
A data fraction of 1 corresponds to all data.
"""
dropout_values = []
parts = dropout_str.strip().split(',')

try:
if len(parts) < 2:
raise Exception("dropout proportion string must specify "
"at least the start and end dropouts")

# Starting dropout proportion
dropout_values.append((0, float(parts[0])))
for i in range(1, len(parts) - 1):
value_x_pair = parts[i].split('@')
if len(value_x_pair) == 1:
# Dropout proportion at half of training
dropout_proportion = float(value_x_pair[0])
data_fraction = 0.5
else:
assert len(value_x_pair) == 2

dropout_proportion = float(value_x_pair[0])
data_fraction = float(value_x_pair[1])

if (data_fraction < dropout_values[-1][0]
or data_fraction > 1.0):
logger.error(
"Failed while parsing value %s in dropout-schedule. "
"dropout-schedule must be in incresing "
"order of data fractions.", value_x_pair)
raise ValueError

dropout_values.append((data_fraction, float(dropout_proportion)))

dropout_values.append((1.0, float(parts[-1])))
except Exception:
logger.error("Unable to parse dropout proportion string %s. "
"See help for option "
"--trainer.dropout-schedule.", dropout_str)
raise

# reverse sort so that its easy to retrieve the dropout proportion
# for a particular data fraction
dropout_values.reverse()
for data_fraction, proportion in dropout_values:
assert data_fraction <= 1.0 and data_fraction >= 0.0
assert proportion <= 1.0 and proportion >= 0.0

return dropout_values


def _get_component_dropout(dropout_schedule, data_fraction):
"""Retrieve dropout proportion from schedule when data_fraction
proportion of data is seen. This value is obtained by using a
piecewise linear function on the dropout schedule.
This is a module-internal function called by _get_dropout_proportions().

See help for --trainer.dropout-schedule for how the dropout value
is obtained from the options.

Arguments:
dropout_schedule: A list of (data_fraction, dropout_proportion) values
sorted in descending order of data_fraction.
data_fraction: The fraction of data seen until this stage of
training.
"""
if data_fraction == 0:
# Dropout at start of the iteration is in the last index of
# dropout_schedule
assert dropout_schedule[-1][0] == 0
return dropout_schedule[-1][1]
try:
# Find lower bound of the data_fraction. This is the
# lower end of the piecewise linear function.
(dropout_schedule_index, initial_data_fraction,
initial_dropout) = next((i, tup[0], tup[1])
for i, tup in enumerate(dropout_schedule)
if tup[0] <= data_fraction)
except StopIteration:
raise RuntimeError(
"Could not find data_fraction in dropout schedule "
"corresponding to data_fraction {0}.\n"
"Maybe something wrong with the parsed "
"dropout schedule {1}.".format(data_fraction, dropout_schedule))

if dropout_schedule_index == 0:
assert dropout_schedule[0][0] == 1 and data_fraction == 1
return dropout_schedule[0][1]

# The upper bound of data_fraction is at the index before the
# lower bound.
final_data_fraction, final_dropout = dropout_schedule[
dropout_schedule_index - 1]

if final_data_fraction == initial_data_fraction:
assert data_fraction == initial_data_fraction
return initial_dropout

assert (data_fraction >= initial_data_fraction
and data_fraction < final_data_fraction)

return ((data_fraction - initial_data_fraction)
* (final_dropout - initial_dropout)
/ (final_data_fraction - initial_data_fraction)
+ initial_dropout)


def _get_dropout_proportions(dropout_schedule, data_fraction):
"""Returns dropout proportions based on the dropout_schedule for the
fraction of data seen at this stage of training.
Returns None if dropout_schedule is None.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please give a couple of examples of what this function might return for different inputs, covering different types of input? e.g. (and this will be wrong):

e.g.:
 _get_dropout_proportions('0.0,0.5,0.0', 0.75) = [ ('*', 0.75) ]
 _get_dropout_proportions('*=0.0,0.5,0.0,lstm.*=0.0,0.3@0.75,0.0', 0.75) = \
          [ ('*', 0.75), ('lstm.*', 0.3) ]

IMO it's always a good idea for this type of code to give such examples, it will
make maintenance much easier. Please give examples for other functions in this
module; and remember to cover trivial cases such as where the input is the
empty string; there may be situations where 3 or 4 examples are needed to
demonstrate the function's range of behavior (but of course we'll
assume the reader is smart enough to extrapolate things).

@danpovey danpovey Jan 21, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... actually, here's an idea (this is similar to something I did in the xconfig code),
How about having a function called _self_test(), that will actually test all of these examples, e.g.

def _self_test():
      assert  _get_dropout_proportions('*=0.0,0.5,0.0,lstm.*=0.0,0.3@0.75,0.0', 0.75) == \
          [ ('*', 0.75), ('lstm.*', 0.3) ]

and have it called directly from __main__ so we can check that it works.
Then the documentation for the function can just say 'see _self_test() for examples'.
That way we will have confidence that the examples are actually correct.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I added self_test. Should it be called every time the module is imported on only when run?

Calls _get_component_dropout() for the different component name patterns
in dropout_schedule.

Arguments:
dropout_schedule: Value for the --trainer.dropout-schedule option.
See help for --trainer.dropout-schedule.
data_fraction: The fraction of data seen until this stage of
training.
"""
if dropout_schedule is None:
return None
dropout_schedule = _parse_dropout_option(dropout_schedule)
dropout_proportions = []
for component_name, component_dropout_schedule in dropout_schedule:
dropout_proportions.append(
(component_name, _get_component_dropout(
component_dropout_schedule, data_fraction)))
return dropout_proportions


def get_dropout_edit_string(dropout_schedule, data_fraction, iter_):
"""Return an nnet3-copy --edits line to modify raw_model_string to
set dropout proportions according to dropout_proportions.

Arguments:
dropout_schedule: Value for the --trainer.dropout-schedule option.
See help for --trainer.dropout-schedule.

See ReadEditConfig() in nnet3/nnet-utils.h to see how
set-dropout-proportion directive works.
"""

if dropout_schedule is None:
return ""

dropout_proportions = _get_dropout_proportions(
dropout_schedule, data_fraction)

edit_config_lines = []
dropout_info = []

for component_name, dropout_proportion in dropout_proportions:
edit_config_lines.append(
"set-dropout-proportion name={0} proportion={1}".format(
component_name, dropout_proportion))
dropout_info.append("pattern/dropout-proportion={0}/{1}".format(
component_name, dropout_proportion))

logger.info("On iteration %d, %s", iter_, ', '.join(dropout_info))
return ("""nnet3-copy --edits='{edits}' - - |""".format(
edits=";".join(edit_config_lines)))
22 changes: 17 additions & 5 deletions egs/wsj/s5/steps/libs/nnet3/train/frame_level_objf/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def train_one_iteration(dir, iter, srand, egs_dir,
run_opts,
cv_minibatch_size=256, frames_per_eg=-1,
min_deriv_time=None, max_deriv_time=None,
shrinkage_value=1.0,
shrinkage_value=1.0, dropout_edit_string="",
get_raw_nnet_from_am=True,
background_process_handler=None):
""" Called from steps/nnet3/train_*.py scripts for one iteration of neural
Expand Down Expand Up @@ -172,9 +172,10 @@ def train_one_iteration(dir, iter, srand, egs_dir,
if os.path.exists('{0}/srand'.format(dir)):
try:
saved_srand = int(open('{0}/srand'.format(dir)).readline().strip())
except (IOError, ValueError) as e:
raise Exception("Exception while reading the random seed "
"for training: {0}".format(e.str()))
except (IOError, ValueError):
logger.error("Exception while reading the random seed "
"for training")
raise
if srand != saved_srand:
logger.warning("The random seed provided to this iteration "
"(srand={0}) is different from the one saved last "
Expand Down Expand Up @@ -248,6 +249,8 @@ def train_one_iteration(dir, iter, srand, egs_dir,
"{dir}/{iter}.raw - |".format(
lr=learning_rate, dir=dir, iter=iter))

raw_model_string = '{0} {1}'.format(raw_model_string, dropout_edit_string)

if do_average:
cur_minibatch_size = minibatch_size
cur_max_param_change = max_param_change
Expand All @@ -265,6 +268,15 @@ def train_one_iteration(dir, iter, srand, egs_dir,
except OSError:
pass

shrink_info_str = ''
if shrinkage_value != 1.0:
shrink_info_str = ' and shrink value is {0}'.format(shrinkage_value)

logger.info("On iteration {0}, learning rate is {1}"
"{shrink_info}.".format(
iter, learning_rate,
shrink_info=shrink_info_str))

train_new_models(dir=dir, iter=iter, srand=srand, num_jobs=num_jobs,
num_archives_processed=num_archives_processed,
num_archives=num_archives,
Expand Down Expand Up @@ -465,7 +477,7 @@ def combine_models(dir, num_iters, models_to_combine, egs_dir,

models_to_combine.add(num_iters)

for iter in models_to_combine:
for iter in sorted(models_to_combine):
if get_raw_nnet_from_am:
model_file = '{0}/{1}.mdl'.format(dir, iter)
if not os.path.exists(model_file):
Expand Down
Loading