2 Part 1. Prepare Training Data
In ‘gibbonR’ there are two ways that you can format your training data. The first can be a set of labelled .wav clips with the class indicated in the name of the file (e.g., ‘gibbon_01.wav’ and ‘noise_01.wav’). The second is to have a folder of selection tables created in Raven Pro (K. Lisa Yang Center for Conservation Bioacoustics) and a folder with the associated ‘.wav’ files. For the second approach there must be an annotation column indicating the call type and it is assumed that all signals of interest are annotated, and the rest of the files contain only background noise.
2.2 Download the data from Github
# You need to tell R where to store the zip files on your computer.
destination.file.path.zip <-
"data/BorneoExampleData.zip"
# You also need to tell R where to save the unzipped files
destination.file.path <- "data/"
# This function will download the data from github
utils::download.file("https://github.com/DenaJGibbon/BorneoExampleData/archive/master.zip",
destfile = destination.file.path.zip)
# This function will unzip the file
utils::unzip(zipfile = destination.file.path.zip,
exdir = destination.file.path)
# Examine the contents
list.of.sound.files <- list.files(paste(destination.file.path,
"BorneoExampleData-master", "data", sep =
"/"),
full.names = T)
list.of.sound.files
Use this function to read in the .RDA file and save it as an R object from https://stackoverflow.com/questions/5577221/how-can-i-load-an-object-into-a-variable-name-that-i-specify-from-an-r-data-file
loadRData <- function(fileName) {
#loads an RData file, and returns it
load(fileName)
get(ls()[ls() != "fileName"])
}
This function will load the entire list of r data files
library(gibbonR)
# You also need to tell R where to save the unzipped files
destination.file.path <- "data/"
# Examine the contents
list.of.sound.files <- list.files(paste(destination.file.path,
"BorneoExampleData-master", "data", sep =
"/"),
full.names = T)
list.of.sound.files
## [1] "data//BorneoExampleData-master/data/multi.class.training.rda"
## [2] "data//BorneoExampleData-master/data/S11_20180219_060002_1800sto3600s.rda"
list.rda.files <- list()
for(x in 1:length(list.of.sound.files)){
list.rda.files[[x]] <- loadRData(list.of.sound.files[[x]])
}
Assign each rda an informative name
Now we create a directory with the training .wav files
2.3 Part 1A. Training Data with Labeled .wav clips
2.3.1 Read in clips and calculate MFCCs
TrainingDataDirectory <- "data/BorneoMultiClass/"
trainingdata <- gibbonR::MFCCFunction(input.dir=TrainingDataDirectory, min.freq = 400, max.freq = 1600,win.avg="standard")
# Convert to a factor
trainingdata$class <- as.factor(trainingdata$class)
# Output a table to see sample size
table(trainingdata$class)
2.3.2 Compare Random Forest and Support Vector Machine for Supervised Classification
Note that this approach uses k-fold cross-validation by splitting the training data into two splits over multiple iterations.
# Set seed for reproducilibility
set.seed(3)
# Create an index to randomly sample 25 observations for test data
testindex <- sample(1:75,25,replace = FALSE)
# Isolate test data samples
testdata <- trainingdata[testindex,]
# Remove from training data
trainingdata_sub <- trainingdata[-testindex,]
# Ensure class is a factor
trainingdata_sub$class <- as.factor(trainingdata_sub$class)
# Run SVM
ml.model.svm <- e1071::svm(trainingdata_sub[, 2:ncol(trainingdata_sub)], trainingdata_sub$class, kernel = "radial",
cross = 25,
probability = TRUE)
# Predict SVM on test data
svm.predict.labels <- predict(ml.model.svm,testdata[,-c(1)])
# Create a confusion matrix
caret::confusionMatrix(svm.predict.labels,testdata$class)
## Confusion Matrix and Statistics
##
## Reference
## Prediction female.gibbon leaf.monkey noise solo.gibbon
## female.gibbon 7 0 0 0
## leaf.monkey 0 1 0 0
## noise 1 1 8 0
## solo.gibbon 0 0 0 7
##
## Overall Statistics
##
## Accuracy : 0.92
## 95% CI : (0.7397, 0.9902)
## No Information Rate : 0.32
## P-Value [Acc > NIR] : 5.992e-10
##
## Kappa : 0.8858
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: female.gibbon Class: leaf.monkey Class: noise
## Sensitivity 0.8750 0.5000 1.0000
## Specificity 1.0000 1.0000 0.8824
## Pos Pred Value 1.0000 1.0000 0.8000
## Neg Pred Value 0.9444 0.9583 1.0000
## Prevalence 0.3200 0.0800 0.3200
## Detection Rate 0.2800 0.0400 0.3200
## Detection Prevalence 0.2800 0.0400 0.4000
## Balanced Accuracy 0.9375 0.7500 0.9412
## Class: solo.gibbon
## Sensitivity 1.00
## Specificity 1.00
## Pos Pred Value 1.00
## Neg Pred Value 1.00
## Prevalence 0.28
## Detection Rate 0.28
## Detection Prevalence 0.28
## Balanced Accuracy 1.00
# Run Random Forest
ml.model.rf <- randomForest::randomForest(x=trainingdata_sub[, 2:ncol(trainingdata_sub)], y = trainingdata_sub$class)
# Predict RF on test data
rf.predict.labels <- predict(ml.model.rf,testdata[,-c(1)])
# Create a confusion matrix
caret::confusionMatrix(rf.predict.labels,testdata$class)
## Confusion Matrix and Statistics
##
## Reference
## Prediction female.gibbon leaf.monkey noise solo.gibbon
## female.gibbon 7 0 0 0
## leaf.monkey 0 1 0 1
## noise 1 1 8 1
## solo.gibbon 0 0 0 5
##
## Overall Statistics
##
## Accuracy : 0.84
## 95% CI : (0.6392, 0.9546)
## No Information Rate : 0.32
## P-Value [Acc > NIR] : 1.197e-07
##
## Kappa : 0.7738
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: female.gibbon Class: leaf.monkey Class: noise
## Sensitivity 0.8750 0.5000 1.0000
## Specificity 1.0000 0.9565 0.8235
## Pos Pred Value 1.0000 0.5000 0.7273
## Neg Pred Value 0.9444 0.9565 1.0000
## Prevalence 0.3200 0.0800 0.3200
## Detection Rate 0.2800 0.0400 0.3200
## Detection Prevalence 0.2800 0.0800 0.4400
## Balanced Accuracy 0.9375 0.7283 0.9118
## Class: solo.gibbon
## Sensitivity 0.7143
## Specificity 1.0000
## Pos Pred Value 1.0000
## Neg Pred Value 0.9000
## Prevalence 0.2800
## Detection Rate 0.2000
## Detection Prevalence 0.2000
## Balanced Accuracy 0.8571
2.4 Part 1B. Training Data with Raven Selection Tables
2.4.1 Prepare training data from labeled annotations
The script below points to a Raven selection table and a correspond .wav file, and uses the selection table to cut out the shorter clips based on the annotation.
# Specify the folder where the training data will be saved
TrainingDataFolderLocation <- paste(FolderPath,"/TrainingDataFromRavenSelectionTables/",
sep='')
# Directory with annotated selection tables
AnnotatedSelectionTables <- list.files( paste(FolderPath,"SelectionTables/GibbonTrainingSelectionTables/",sep=''),
full.names = T)
# Directory with corresponding .wav files
AnnotatedWaveFiles <- list.files(paste(FolderPath,"GibbonTrainingFiles/",sep=''),
full.names = T)
AnnotatedWaveFilesShort <- basename(AnnotatedWaveFiles)
AnnotatedWaveFilesShort <- str_split_fixed(AnnotatedWaveFilesShort,pattern = '.wav', n=2)[,1]
# Loop to cut out the corresponding annotations into short clips
for(i in 1: length(AnnotatedSelectionTables)){
# Read in selection table
TempSelectionTable <- read.delim2(AnnotatedSelectionTables[i])
# Find the corresponding soundfile
SoundFileIndex <- which(str_detect(AnnotatedSelectionTables[i],AnnotatedWaveFilesShort))
TempAnnotateWave <- readWave(AnnotatedWaveFiles[SoundFileIndex])
ShortSoundClips <- lapply(1:nrow(TempSelectionTable),
function(j) extractWave(TempAnnotateWave,
from= as.numeric(TempSelectionTable[j,]$Begin.Time..s.),
to=as.numeric(TempSelectionTable[j,]$ End.Time..s.),
xunit = c("time"),plot=F,output="Wave"))
# Write wave files to folder
for(k in 1:length(ShortSoundClips)){
TempClip <- ShortSoundClips[[k]]
WavFileName <- paste(TrainingDataFolderLocation,'/female.gibbon_', k, '.wav',sep="")
writeWave(TempClip,WavFileName,extensible = F)
}
}
2.4.2 Prepare noise training data from files without target signal
The script below uses a band-limited energy detector to identify sounds in the frequency range of interest. The file included does not have any gibbon calls, so all sounds detected will be non-gibbon or noise.
# Specify the folder where the training data will be saved (same as above)
TrainingDataFolderLocation <- paste(FolderPath,"/TrainingDataFromRavenSelectionTables/",
sep='')
# Directory with annotated selection tables
NoiseSelectionTables <- list.files( paste(FolderPath,"/SelectionTables/NoiseSelectionTables/",sep=''),
full.names = T)
# Directory with corresponding .wav files
NoiseWaveFiles <- list.files(paste(FolderPath,"/NoiseFiles/",sep=''),full.names = TRUE)
NoiseWaveFilesShort <- basename(NoiseWaveFiles)
NoiseWaveFilesShort <- str_split_fixed(NoiseWaveFilesShort,pattern = '.wav', n=2)[,1]
for(i in 1:length(NoiseSelectionTables)){
# Find the corresponding soundfile
SoundFileIndex <- which(str_detect(NoiseSelectionTables[i],NoiseWaveFilesShort))
DetectBLED(input=NoiseWaveFiles[SoundFileIndex],
min.freq = 400,
max.freq = 1600,
noise.quantile.val=0.3,
spectrogram.window =512,
pattern.split = ".wav",
min.signal.dur = 3,
max.sound.event.dur = 12,
wav.output = "TRUE",
output.dir = TrainingDataFolderLocation,
swift.time=TRUE,
time.start=06,
time.stop=11,
write.table.output=TRUE,
verbose=TRUE,
random.sample=FALSE)
}
2.4.3 Now read in clips based on Raven Selection tables and calculate MFCCs
TrainingDataFolderLocation <- paste(FolderPath,"/TrainingDataFromRavenSelectionTables/",
sep='')
trainingdata <- gibbonR::MFCCFunction(input.dir=TrainingDataFolderLocation, min.freq = 400, max.freq = 1600,win.avg="standard")
trainingdata$class <- as.factor(trainingdata$class)
table(trainingdata$class )
2.4.4 Compare Random Forest and Support Vector Machine for Supervised Classification
# Set seed for reproducibility
set.seed(3)
# Check the structure (this is binary with only two classes)
table(trainingdata$class)
##
## female.gibbon noise
## 26 27
# Create an index to randomly sample 25 observations for test data
testindex <- sample(1:53,25,replace = FALSE)
# Isolate test data samples
testdata <- trainingdata[testindex,]
# Remove from training data
trainingdata_sub <- trainingdata[-testindex,]
# Ensure class is a factor
trainingdata_sub$class <- as.factor(trainingdata_sub$class)
# Run SVM
ml.model.svm <- e1071::svm(trainingdata_sub[, 2:ncol(trainingdata_sub)], trainingdata_sub$class, kernel = "radial",
cross = 25,
probability = TRUE)
# Predict SVM on test data
svm.predict.labels <- predict(ml.model.svm,testdata[,-c(1)])
# Create a confusion matrix
caret::confusionMatrix(svm.predict.labels,testdata$class)
## Confusion Matrix and Statistics
##
## Reference
## Prediction female.gibbon noise
## female.gibbon 11 0
## noise 0 14
##
## Accuracy : 1
## 95% CI : (0.8628, 1)
## No Information Rate : 0.56
## P-Value [Acc > NIR] : 5.066e-07
##
## Kappa : 1
##
## Mcnemar's Test P-Value : NA
##
## Sensitivity : 1.00
## Specificity : 1.00
## Pos Pred Value : 1.00
## Neg Pred Value : 1.00
## Prevalence : 0.44
## Detection Rate : 0.44
## Detection Prevalence : 0.44
## Balanced Accuracy : 1.00
##
## 'Positive' Class : female.gibbon
##
# Run Random Forest
ml.model.rf <- randomForest::randomForest(x=trainingdata_sub[, 2:ncol(trainingdata_sub)], y = trainingdata_sub$class)
# Predict RF on test data
rf.predict.labels <- predict(ml.model.rf,testdata[,-c(1)])
# Create a confusion matrix
caret::confusionMatrix(rf.predict.labels,testdata$class)
## Confusion Matrix and Statistics
##
## Reference
## Prediction female.gibbon noise
## female.gibbon 11 3
## noise 0 11
##
## Accuracy : 0.88
## 95% CI : (0.6878, 0.9745)
## No Information Rate : 0.56
## P-Value [Acc > NIR] : 0.0006695
##
## Kappa : 0.7634
##
## Mcnemar's Test P-Value : 0.2482131
##
## Sensitivity : 1.0000
## Specificity : 0.7857
## Pos Pred Value : 0.7857
## Neg Pred Value : 1.0000
## Prevalence : 0.4400
## Detection Rate : 0.4400
## Detection Prevalence : 0.5600
## Balanced Accuracy : 0.8929
##
## 'Positive' Class : female.gibbon
##