提交 d91dab00 编写于 作者: X Xinghai Sun 提交者: GitHub

Merge pull request #74 from qingqing01/ds2

Support variable input batch and SortaGrad.
...@@ -8,6 +8,7 @@ import json ...@@ -8,6 +8,7 @@ import json
import random import random
import soundfile import soundfile
import numpy as np import numpy as np
import itertools
import os import os
RANDOM_SEED = 0 RANDOM_SEED = 0
...@@ -62,6 +63,7 @@ class DataGenerator(object): ...@@ -62,6 +63,7 @@ class DataGenerator(object):
self.__stride_ms__ = stride_ms self.__stride_ms__ = stride_ms
self.__window_ms__ = window_ms self.__window_ms__ = window_ms
self.__max_frequency__ = max_frequency self.__max_frequency__ = max_frequency
self.__epoc__ = 0
self.__random__ = random.Random(RANDOM_SEED) self.__random__ = random.Random(RANDOM_SEED)
# load vocabulary (dictionary) # load vocabulary (dictionary)
self.__vocab_dict__, self.__vocab_list__ = \ self.__vocab_dict__, self.__vocab_list__ = \
...@@ -245,10 +247,42 @@ class DataGenerator(object): ...@@ -245,10 +247,42 @@ class DataGenerator(object):
new_batch.append((padded_audio, text)) new_batch.append((padded_audio, text))
return new_batch return new_batch
def instance_reader_creator(self, def __batch_shuffle__(self, manifest, batch_size):
manifest_path, """
sort_by_duration=True, The instances have different lengths and they cannot be
shuffle=False): combined into a single matrix multiplication. It usually
sorts the training examples by length and combines only
similarly-sized instances into minibatches, pads with
silence when necessary so that all instances in a batch
have the same length. This batch shuffle fuction is used
to make similarly-sized instances into minibatches and
make a batch-wise shuffle.
1. Sort the audio clips by duration.
2. Generate a random number `k`, k in [0, batch_size).
3. Randomly remove `k` instances in order to make different mini-batches,
then make minibatches and each minibatch size is batch_size.
4. Shuffle the minibatches.
:param manifest: manifest file.
:type manifest: list
:param batch_size: Batch size. This size is also used for generate
a random number for batch shuffle.
:type batch_size: int
:return: batch shuffled mainifest.
:rtype: list
"""
manifest.sort(key=lambda x: x["duration"])
shift_len = self.__random__.randint(0, batch_size - 1)
batch_manifest = zip(*[iter(manifest[shift_len:])] * batch_size)
self.__random__.shuffle(batch_manifest)
batch_manifest = list(sum(batch_manifest, ()))
res_len = len(manifest) - shift_len - len(batch_manifest)
batch_manifest.extend(manifest[-res_len:])
batch_manifest.extend(manifest[0:shift_len])
return batch_manifest
def instance_reader_creator(self, manifest):
""" """
Instance reader creator for audio data. Creat a callable function to Instance reader creator for audio data. Creat a callable function to
produce instances of data. produce instances of data.
...@@ -256,32 +290,13 @@ class DataGenerator(object): ...@@ -256,32 +290,13 @@ class DataGenerator(object):
Instance: a tuple of a numpy ndarray of audio spectrogram and a list of Instance: a tuple of a numpy ndarray of audio spectrogram and a list of
tokenized and indexed transcription text. tokenized and indexed transcription text.
:param manifest_path: Filepath of manifest for audio clip files. :param manifest: Filepath of manifest for audio clip files.
:type manifest_path: basestring :type manifest: basestring
:param sort_by_duration: Sort the audio clips by duration if set True
(for SortaGrad).
:type sort_by_duration: bool
:param shuffle: Shuffle the audio clips if set True.
:type shuffle: bool
:return: Data reader function. :return: Data reader function.
:rtype: callable :rtype: callable
""" """
if sort_by_duration and shuffle:
sort_by_duration = False
logger.warn("When shuffle set to true, "
"sort_by_duration is forced to set False.")
def reader(): def reader():
# read manifest
manifest = self.__read_manifest__(
manifest_path=manifest_path,
max_duration=self.__max_duration__,
min_duration=self.__min_duration__)
# sort (by duration) or shuffle manifest
if sort_by_duration:
manifest.sort(key=lambda x: x["duration"])
if shuffle:
self.__random__.shuffle(manifest)
# extract spectrogram feature # extract spectrogram feature
for instance in manifest: for instance in manifest:
spectrogram = self.__audio_featurize__( spectrogram = self.__audio_featurize__(
...@@ -296,8 +311,8 @@ class DataGenerator(object): ...@@ -296,8 +311,8 @@ class DataGenerator(object):
batch_size, batch_size,
padding_to=-1, padding_to=-1,
flatten=False, flatten=False,
sort_by_duration=True, sortagrad=False,
shuffle=False): batch_shuffle=False):
""" """
Batch data reader creator for audio data. Creat a callable function to Batch data reader creator for audio data. Creat a callable function to
produce batches of data. produce batches of data.
...@@ -317,20 +332,32 @@ class DataGenerator(object): ...@@ -317,20 +332,32 @@ class DataGenerator(object):
:param flatten: If set True, audio data will be flatten to be a 1-dim :param flatten: If set True, audio data will be flatten to be a 1-dim
ndarray. Otherwise, 2-dim ndarray. Default is False. ndarray. Otherwise, 2-dim ndarray. Default is False.
:type flatten: bool :type flatten: bool
:param sort_by_duration: Sort the audio clips by duration if set True :param sortagrad: Sort the audio clips by duration in the first epoc
(for SortaGrad). if set True.
:type sort_by_duration: bool :type sortagrad: bool
:param shuffle: Shuffle the audio clips if set True. :param batch_shuffle: Shuffle the audio clips if set True. It is
:type shuffle: bool not a thorough instance-wise shuffle, but a
specific batch-wise shuffle. For more details,
please see `__batch_shuffle__` function.
:type batch_shuffle: bool
:return: Batch reader function, producing batches of data when called. :return: Batch reader function, producing batches of data when called.
:rtype: callable :rtype: callable
""" """
def batch_reader(): def batch_reader():
instance_reader = self.instance_reader_creator( # read manifest
manifest = self.__read_manifest__(
manifest_path=manifest_path, manifest_path=manifest_path,
sort_by_duration=sort_by_duration, max_duration=self.__max_duration__,
shuffle=shuffle) min_duration=self.__min_duration__)
# sort (by duration) or shuffle manifest
if self.__epoc__ == 0 and sortagrad:
manifest.sort(key=lambda x: x["duration"])
elif batch_shuffle:
manifest = self.__batch_shuffle__(manifest, batch_size)
instance_reader = self.instance_reader_creator(manifest)
batch = [] batch = []
for instance in instance_reader(): for instance in instance_reader():
batch.append(instance) batch.append(instance)
...@@ -339,6 +366,7 @@ class DataGenerator(object): ...@@ -339,6 +366,7 @@ class DataGenerator(object):
batch = [] batch = []
if len(batch) > 0: if len(batch) > 0:
yield self.__padding_batch__(batch, padding_to, flatten) yield self.__padding_batch__(batch, padding_to, flatten)
self.__epoc__ += 1
return batch_reader return batch_reader
......
...@@ -93,23 +93,27 @@ def train(): ...@@ -93,23 +93,27 @@ def train():
""" """
DeepSpeech2 training. DeepSpeech2 training.
""" """
# initialize data generator # initialize data generator
data_generator = DataGenerator( def data_generator():
vocab_filepath=args.vocab_filepath, return DataGenerator(
normalizer_manifest_path=args.normalizer_manifest_path, vocab_filepath=args.vocab_filepath,
normalizer_num_samples=200, normalizer_manifest_path=args.normalizer_manifest_path,
max_duration=20.0, normalizer_num_samples=200,
min_duration=0.0, max_duration=20.0,
stride_ms=10, min_duration=0.0,
window_ms=20) stride_ms=10,
window_ms=20)
train_generator = data_generator()
test_generator = data_generator()
# create network config # create network config
dict_size = data_generator.vocabulary_size() dict_size = train_generator.vocabulary_size()
# paddle.data_type.dense_array is used for variable batch input.
# the size 161 * 161 is only an placeholder value and the real shape
# of input batch data will be set at each batch.
audio_data = paddle.layer.data( audio_data = paddle.layer.data(
name="audio_spectrogram", name="audio_spectrogram", type=paddle.data_type.dense_array(161 * 161))
height=161,
width=2000,
type=paddle.data_type.dense_vector(322000))
text_data = paddle.layer.data( text_data = paddle.layer.data(
name="transcript_text", name="transcript_text",
type=paddle.data_type.integer_value_sequence(dict_size)) type=paddle.data_type.integer_value_sequence(dict_size))
...@@ -136,28 +140,16 @@ def train(): ...@@ -136,28 +140,16 @@ def train():
cost=cost, parameters=parameters, update_equation=optimizer) cost=cost, parameters=parameters, update_equation=optimizer)
# prepare data reader # prepare data reader
train_batch_reader_sortagrad = data_generator.batch_reader_creator( train_batch_reader = train_generator.batch_reader_creator(
manifest_path=args.train_manifest_path,
batch_size=args.batch_size,
padding_to=2000,
flatten=True,
sort_by_duration=True,
shuffle=False)
train_batch_reader_nosortagrad = data_generator.batch_reader_creator(
manifest_path=args.train_manifest_path, manifest_path=args.train_manifest_path,
batch_size=args.batch_size, batch_size=args.batch_size,
padding_to=2000, sortagrad=True if args.init_model_path is None else False,
flatten=True, batch_shuffle=True)
sort_by_duration=False, test_batch_reader = test_generator.batch_reader_creator(
shuffle=True)
test_batch_reader = data_generator.batch_reader_creator(
manifest_path=args.dev_manifest_path, manifest_path=args.dev_manifest_path,
batch_size=args.batch_size, batch_size=args.batch_size,
padding_to=2000, batch_shuffle=False)
flatten=True, feeding = train_generator.data_name_feeding()
sort_by_duration=False,
shuffle=False)
feeding = data_generator.data_name_feeding()
# create event handler # create event handler
def event_handler(event): def event_handler(event):
...@@ -183,17 +175,8 @@ def train(): ...@@ -183,17 +175,8 @@ def train():
time.time() - start_time, event.pass_id, result.cost) time.time() - start_time, event.pass_id, result.cost)
# run train # run train
# first pass with sortagrad
if args.use_sortagrad:
trainer.train(
reader=train_batch_reader_sortagrad,
event_handler=event_handler,
num_passes=1,
feeding=feeding)
args.num_passes -= 1
# other passes without sortagrad
trainer.train( trainer.train(
reader=train_batch_reader_nosortagrad, reader=train_batch_reader,
event_handler=event_handler, event_handler=event_handler,
num_passes=args.num_passes, num_passes=args.num_passes,
feeding=feeding) feeding=feeding)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册