optimizePars {soundgen} | R Documentation |
Optimize parameters for acoustic analysis
Description
This customized wrapper for optim
attempts to optimize
the parameters of segment
or analyze
by comparing
the results with a manually annotated "key". This optimization function uses
a single measurement per audio file (e.g., median pitch or the number of
syllables). For other purposes, you may want to adapt the optimization
function so that the key specifies the exact timing of syllables, their
median length, frame-by-frame pitch values, or any other characteristic that
you want to optimize for. The general idea remains the same, however: we want
to tune function parameters to fit our type of audio and research priorities.
The default settings of segment
and analyze
have
been optimized for human non-linguistic vocalizations.
Usage
optimizePars(
myfolder,
key,
myfun,
pars,
bounds = NULL,
fitnessPar,
fitnessFun = function(x) 1 - cor(x, key, use = "pairwise.complete.obs"),
nIter = 10,
init = NULL,
initSD = 0.2,
control = list(maxit = 50, reltol = 0.01, trace = 0),
otherPars = list(plot = FALSE),
mygrid = NULL,
verbose = TRUE
)
Arguments
myfolder |
path to where the .wav files live |
key |
a vector containing the "correct" measurement that we are aiming to reproduce |
myfun |
the function being optimized: either 'segment' or 'analyze' (in quotes) |
pars |
names of arguments to |
bounds |
a list setting the lower and upper boundaries for possible
values of optimized parameters. For ex., if we optimize |
fitnessPar |
the name of output variable that we are comparing with the key, e.g. 'nBursts' or 'pitch_median' |
fitnessFun |
the function used to evaluate how well the output of
|
nIter |
repeat the optimization several times to check convergence |
init |
initial values of optimized parameters (if NULL, the default
values are taken from the definition of |
initSD |
each optimization begins with a random seed, and
|
control |
a list of control parameters passed on to
|
otherPars |
a list of additional arguments to |
mygrid |
a dataframe with one column per parameter to optimize, with
each row specifying the values to try. If not NULL, |
verbose |
if TRUE, reports the values of parameters evaluated and fitness |
Details
If your sounds are very different from human non-linguistic vocalizations, you may want to change the default values of other arguments to speed up convergence. Adapt the code to enforce suitable constraints, depending on your data.
Value
Returns a matrix with one row per iteration with fitness in the first column and the best values of each of the optimized parameters in the remaining columns.
Examples
## Not run:
# Download 260 sounds from the supplements in Anikin & Persson (2017)
# - see http://cogsci.se/publications.html
# Unzip them into a folder, say '~/Downloads/temp'
myfolder = '~/Downloads/temp260' # 260 .wav files live here
# Optimization of SEGMENTATION
# Import manual counts of syllables in 260 sounds from
# Anikin & Persson (2017) (our "key")
key = segmentManual # a vector of 260 integers
# Run optimization loop several times with random initial values
# to check convergence
# NB: with 260 sounds and default settings, this might take ~20 min per iteration!
res = optimizePars(myfolder = myfolder, myfun = 'segment', key = key,
pars = c('shortestSyl', 'shortestPause'),
fitnessPar = 'nBursts', otherPars = list(method = 'env'),
nIter = 3, control = list(maxit = 50, reltol = .01, trace = 0))
# Examine the results
print(res)
for (c in 2:ncol(res)) {
plot(res[, c], res[, 1], main = colnames(res)[c])
}
pars = as.list(res[1, 2:ncol(res)]) # top candidate (best pars)
s = do.call(segment, c(myfolder, pars)) # segment with best pars
cor(key, as.numeric(s[, fitnessPar]))
boxplot(as.numeric(s[, fitnessPar]) ~ as.integer(key), xlab='key')
abline(a=0, b=1, col='red')
# Try a grid with particular parameter values instead of formal optimization
res = optimizePars(myfolder = myfolder, myfun = 'segment', key = segmentManual,
pars = c('shortestSyl', 'shortestPause'),
fitnessPar = 'nBursts', otherPars = list(method = 'env'),
mygrid = expand.grid(shortestSyl = c(30, 40),
shortestPause = c(30, 40, 50)))
1 - res$fit # correlations with key
# Optimization of PITCH TRACKING (takes several hours!)
key = as.numeric(log(pitchManual))
res = optimizePars(
myfolder = myfolder,
myfun = 'analyze',
key = key, # log-scale better for pitch
pars = c('windowLength', 'silence'),
bounds = list(low = c(5, 0), high = c(200, .2)),
fitnessPar = 'pitch_median',
nIter = 2,
otherPars = list(plot = FALSE, loudness = NULL, novelty = NULL,
roughness = NULL, nFormants = 0),
fitnessFun = function(x) {
1 - cor(log(x), key, use = 'pairwise.complete.obs') *
(1 - mean(is.na(x) & is.finite(key))) # penalize failing to detect f0
})
## End(Not run)