The following work is preliminary and subject to ongoing investigation.
These analyses do not necessarily reflect the point of view of ICCAT or the FAO and in no way anticipate ICCAT or FAO future policy.
Develop a flexible and powerful indicator of stock status for tunas, billfish and sharks that can be tested theoretically and empirically.
Title: ‘Simulation Testing Ecosystem Indicators: Support to ICCAT’s Ecosystem Approach to Fisheries Management (EAFM)’
| Term | Dec 2023 - June 2026 |
| Funding body | International Commission for the Conservation of Atlantic Tunas (ICCAT) |
| Funding stream | FAO Global Environmental Facility (GEF) Common Oceans ABNJ Tuna Project Fund |
| Call No. | # 11903 / 2023 |
| Project Partners | Blue Matter Science Ltd. |
| Blue Matter Team | Tom Carruthers, Adrian Hordyk, Quang Huynh |
| ICCAT Collaborators | Nathan Taylor |
To meet the requirements of the precautionary approach and the ecosystem approach to fisheries management (EBFM), indicators of stock status are needed for secondary species, defined here as those lack sufficient data or capacity to conduct routine stock assessments (see Carruthers et al. 2024 for a detailed problem statement). Any such indicator be sound theoretically and should be validated empirically.
A well-documented, defensible and transparent framework is needed to support tactical decision making to move beyond the single species assessment paradigm and make progress towards the essential goals of EBFM.
The EcoTest project aims to use simulation modelling to identify data types and algorithms that can inform stock status of secondary species and then validate these indicators empirically in cases where there have been defensible stock assessments.
Previous EcoTest research consolidated the dynamics of six stock assessments for species caught in the North Atlantic longline fisheries (swordfish, bigeye tuna, albacore tuna, shortfin mako shark, white marlin and blue marlin) into a multi-species, multi-fleet operating model (Huynh et al. 2022). Those models were used to simulate various future scenarios for fishing and stock dynamics to develop Indicator 1: an indicator specific to the longline case study that could predict stock status for those 6 species without undergoing a full stock assessment (Carruthers et al 2024). The performance of Indicator 1 was generally very good, but it was applicable only to the 6 species of the operating model.
A key advance in developing Indicator 1 was the use of deep learning to train neural network indicators. The large number of simulated fisheries provided both true, simulated stock status and also a wide range of possible input data types (features). This training dataset was essentially a large table, including more than 20 thousand fishery simulations. Each line representing a simulation. The first column contained the true, simulated stock status, the remaining 450+ columns were ‘features’ - potential data inputs that could be available in a real fishery setting for estimating stock status.
In the EcoTest framework, the training dataset serves as a central resource for testing indicators. Those could include proposed data types for use in an indicator or those available for a given case study.
If for a real stock 15 data inputs (features) are available, the training dataset can be subsetted and neural networks trained for that set of inputs to see whether in theory those data could provide sufficient information about stock status. Hence EcoTest has a theoretical test of a proposed indicator built-in.
The flexibility of neural networks alleviates the need to specify and code bespoke fishery dynamics models for every possible combination of data inputs. For example, a neural network indicator can be trained on (and use for prediction) only catches and mean lengths or alternatively, recent CPUE indices and recent catches. The focus is on the information content of the data and the estimation performance of the indicator.
The ETest package is built around Indicator 2: an evolution of the original approach, that includes a testing dataset from a much more general set of simulations encompassing population and fishing dynamics for a wide range of pelagic highly migratory species (sharks, billfish and tunas) Carruthers & Taylor 2025.
EcoTest uses a conventional age-structured models to generate simulations of fisheries over a wide range of life-history and exploitation dynamics. Those age-structured equations are the theoretical basis of the stock assessments models used in the provision of managment advice for data-rich tuna, billfish and shark species. Ie in its current form EcoTest assumes the same underlying theory used in management advice. In principle, other theoretical such as Ecosim-with-Ecopath (Figure below) or Atlantis could be used for simulating fisheries.
Over a very large number of simulations a single year is selected and true, simulated stock status is taken from the model along with simulated data, and observable aspects of life history (e.g. growth and longevity) and fishing dynamics (e.g. selectivity). The outputs are processed into a large training dataset (many thousands of simulations) where each row is a simulation containing a number of columns. The first is the target variable of interest (stock status) and each subsequent column is a potential data input that could be used to estimate the target variable (e.g., slope in catches, current mean length relative to mean, somatic growth etc. etc. ).
The data inputs relate to trajectories, levels and variability of data such as catches, CPUE indices and mean length in catches and are processed and formatted in such a way that they can be calculated for any fishery (e.g. mean length in catches is phrased as a percentage of asymptotic length, slopes in data are slopes in log data etc).
For a given case-study only some of the data inputs (types of data, data streams) of the full training dataset may be available. The training dataset is subsetted to include only those data inputs.
It has been demonstrated that Artificial Neural Networks (ANNs) are able to approximate any continuous function provided they are specified with sufficient number of parameters (width of a single-layer ANN). For this reason they provide a very flexible, model-free approach to estimating stock status from user-specifed data inputs. They can reveal the information content of various data types, determine expected indicator performance and then rapidly provide stock status estimates for a new dataset.
Once an indicator has been established (a set of case-study specific data inputs and a trained neural network) and determined to perform adequately in theory, it may be then validated empirically using established approaches for stock assessment (e.g. stock assessments).
Assuming the indicator passes both theoretical evaluate and empirical validation, data inputs for a real case-study can be formatted and submitted to the indicator to determine stock status.
EcoTest can be used to theoretically test the performance of a proposed set of data inputs for an indicator. The user subsets the training dataset to include only those data inputs and then fits a neural network to evaluate performance using cross-validation.
During training a validation dataset is used to check for model overparameterization and confirm predictive ability for independent data not used by the training algorithm.After training, the expected indicator performance is evaluated according to a completely independent testing dataset.
EcoTest can accept a range of data inputs (features) that may be available for secondary species derived from data streams such as total annual catches, CPUE indices, mean length in catches and mean age in catches (Table 1). These can be provide along with life-history (e.g. somatic growth, natural survival) and fleet-specific (e.g. selectivity) characteristics to potentially improve reliability in interpreting these data inputs. Where multiple fleets can be identified, data inputs can be provided for up to 3 individual fleets. Where multiple stocks can be assumed to have been exploited by a common fishery with comparable exploitation history, multi-stock data inputs can be used including catch ratios of one stock relative to another.
Potentially the least demanding use of the EcoTest approach would be to rapidly obtain preliminary estimates of status for the various secondary species in order to conduct a triage, identifying those for which estimated status is considered below target levels. This could be used to instigate more in-depth investigation of data for those stocks.
The Keras, Tensorflow and Miniconda libraries on which EcoTest methods are based are highly sensitive to the version of R and RStudio.
It is therefore highly recommended that users install the very latest version of R and RStudio before continuing (you will almost certainly have difficulties otherwise).
R can be downloaded from the R Project for Statistical Computing webpage.
Currently the ETest package is compatible with R version R version 4.5.1 (2025-06-13 ucrt).
RStudio can be downloaded from the Posit webpage.
Currently the ETest package is working in RStudio version 2025.05.1 Build 513.
The ETest package has only been tested on a Windows x64 system.
Open RStudio and run the following lines of code at the R command prompt:
# Keras, Tensorflow, Miniconda
install.packages("keras3")
library(keras3)
install_keras()
# EcoTest package ETest
install.packages("remotes")
remotes::install_github('blue-matter/ETest')
To test your installation run the following code
library(ETest)
test = train_ind(Shark_1_data)
If your packages are working you should see something like this:
Lets just assume we have some data on catches, recent CPUE and a bit of life history info. Remember, these names have to match the EcoTest codes for indicator features (see Table 1 below).
library(ETest)
dat = data.frame(
K_s1 = 0.25, # von B. somatic growth (stock 1)
M_K_s1 = 1.05, # Instantaneous natural Mort. / von B. K (stock 1)
L50_Linf_s1 = 0.74, # Length at 50% maturity (spawning fraction) / asymptotic length (L-infinity) (stock 1)
maxa_s1 = 11, # Age at 5% survival (stock 1)
L5_L50_s1_f1 = 0.2, # Shortest length at 5% selectivity relative to length at 50% maturity (stock 1)
C_rel_s1_f1 = 0.67, # Current catches relative to historical average (stock 1, fleet 1)
C_g10_s1_f1 = 0.01, # Slope in log catches over the last 10 years (stock 1, fleet 1)
I_rel_s1_f1 = 0.43, # Current CPUE relative to historical average (stock 1, fleet 1)
I_g20_s1_f1 = -0.09, # Slope in log CPUE over the last 20 years (stock 1, fleet 1)
ML_rel_s1_f1 = 0.9 # Current mean length in catches relative to historical average (stock 1, fleet 1)
)
input = list(dat=dat)
my_indicator = train_ind(input)
You can see that the performance of this indicator isn’t stellar however it correctly classifies stocks below half SSBMSY (an ICCAT limit reference point) and above SSBMSY (an ICCAT target reference point) in about 60% of simulations.
For now we are going to assume we find this acceptable (you might well not!).
We can now obtain a point estimate of stock status using our indicator and the dataset provided:
my_pred = pred_ind(my_indicator)
my_pred
Since only one set of data were provided there is only a point estimate of stock status. However most datasets can be sampled to provide stochastic inputs (bootstrapping etc).
For the purposes of demonstration lets just create some samples of data so you can plot indicator estimates that include uncertainty:
nsamp = 50 # 50 stochastic draws of the dataset
nobs = ncol(dat) # data set has nobs features (data types)
log_norm_err = rlnorm(nobs*nsamp,0,0.2) # demo error
data_repped = rep(as.numeric(dat),each=nsamp) # duplicate data for nsamp rows
dat_s = array(data_repped * log_norm_err,c(nsamp,nobs)) # combine data with errors
dat_s = as.data.frame(dat_s) # make into data frame
names(dat_s) = names(dat) # make sure to copy over labels
input_s = list(dat=dat_s) # make an input object where position one in the list is the data
my_indicator_s = train_ind(input_s) # train the indicator
my_pred_s = pred_ind(my_indicator_s) # make a status prediction for each of the 50 samples of data
hist(my_pred_s, xlab="Estimated SSB/SSBMSY", main="Demonstration Stochastic Indicator", col="cornflowerblue", border="white")
The training dataset ‘TD’ is loaded into your R session when you load the ETest library. The training dataset is very large including more than 20 thousand simulated fisheries and more than 450 features.
dim(TD)
TD[1:3, 1:20]
This training dataset shows the range of conditions that were simulated. These provide an idea of whether the indicator is applicable to a new dataset.
Here for example, is the range of von Bertallanfy growth and natural mortality in the training dataset:
plot(exp(TD$K_s1),exp(TD$K_s1+TD$M_K_s1), xlab = "von B. K", ylab="Nat. Mort.",pch=19, cex=0.5, col="#0000ff50",xlim=c(0.15,0.8))
If you wish to know the theoretical performance of an indicator based on a particular set of input data you can use the train_NN(). Lets try this for the full dataset:
NN1 = train_NN(TD)
The RStudio viewer window shows the progression of the neural network training over 20 epochs (estimation iterations, passes through the backprogation algorithm).
As the training progresses, mean absolute error (MAE) and the value of the loss function (loss) for the training dataset (blue) decreases (predicted log(SSB/SSMSY) gets closer to the simulated log(SSB/SSBMSY)). As the line plateaus, this indicates that the neural network is not finding a better fit to the data.
The green line represents the MAE and loss of the validation dataset, an independent set of data not used in the training. These lines should track one another. If not it is possible that the model is over parameterized (blue line below green line) or that the validation dataset is not representative of the training dataset (see validation_split).
The RStudio plot window shows the simulated vs predicted chart:
Using all the features (all of the data inputs, columns) of the training dataset we can achieve very good stock status estimation performance using the default neural network. If the stock was below half of SSBMSY then the indicator would correctly identify this in around 82% of simulations. If the stock was above SSBMSY, the indicator would correctly identify this in 85% of simulations.
Of course we might not have access to all of those data types. Lets subset the training dataset to see how well an indicator of stock 1 status would work with only stock 1 CPUE, catch and mean length data:
ft = names(TD) # the names of all features
is_stock_1 = grepl("s1",ft) # those features for stock 1 (s1 is the code)
is_ML = grepl("ML_",ft) # those features relating to mean length
is_I = grepl("I_",ft) # those features relating to CPUE indices
is_C = grepl("C_",ft) # those features relating to Catch indices
keep = is_stock_1 & (is_ML | is_I | is_C)
dat_inputs = TD[,keep]
# lets make our new dataset
myTD = cbind(TD$Res, # first column is the response variable
TD$K_s1, # we'll assume we know somatic growth
TD$M_K_s1, # we can get M/K ratio from demographic analysis
TD$maxa_s1, # maximum age is known (age at 5% survival)
TD$L50_Linf_s1, # the ratio of length at 50% maturity to Linfinity is known
dat_inputs) # our data inputs from above
NN2 = train_NN(myTD)
The mean absolute error of the indicator is somewhat higher and the classification errors are higher, but theoretical performance based on just catches, mean length and CPUE is relatively good.
It’s possible that a more complex neural network might provide better performance. The default is 6 nodes in the first layer and 3 in the second layer.
This might seem like a simple neural network and compared the many billion parameter LLMs of openAI, it is. However by conventional fishery modelling standards, this simple model includes a large number of parameters (model weights). The dataset myTD has 58 features in the input layer. That means 6 * 58 weights before the first layer of nodes. There are 6 * 3 between the first and second layers and another 3 before the output layer (the response variable, log(SSB/SSBMSY)).
That means a total of 369 parameters.
But, lets lose our mind for a second and make a much more complicated network and fit it for longer. This one has 2360 parameters:
NN3 = train_NN(myTD, nodes = c(30,20), nepoch = 50)
The neural network is continuing to find a better fit to the training dataset but without seeing any benefits in fit to the validation dataset. This suggests that the performance it is obtaining on the training set is not representative of what can be expected for an independent dataset. The model is over parameterized. The solution in such cases is to either simplify the model, make the dataset larger (more simulations in training and validation dataset) or increase the fraction of simulations in the validation set (parameter validation_split in functions train_NN and train_ind).
In this example we will process some example time series data, train an indicator and estimate stock status using it.
The ETest package comes with an example dataset ‘Some_data’ for these worked examples. The dataset has catches, CPUE indices, mean length, life history and selectivity information for three fleets simultaneously fishing three stocks. We are not necessarily going to assume we have all these data, but they are there to experiment with.
To make creating time series data inputs easier, ETest has a function ts_features() which extracts the levels and slopes processed in the same way as the training dataset.
stock = 1 # Lets just try this for one stock and ...
fleet = 1 # ... one fleet.
Catch = Some_data$Catch[stock,fleet,] # Catch data is a 3D array [stock, fleet, year]
plot(as.numeric(names(Catch)),Catch) # what do these catch data look like?
Catch_f = ts_features(Catch, "C") # lets capture the rel, g5, g10, g20 and g40 features
Catch_f
Index = Some_data$Index[stock,fleet,] # CPUE index data is a 3D array [stock, fleet, year]
Index_f = ts_features(Index, "I") # captures the rel, g5, g10, g20 and g40 features
ML = Some_data$Mean_length[stock,fleet,] # Mean length data
ML_f = ts_features(ML,"ML") # captures the rel, g5, g10, g20 and g40 features
fleet_data = as.data.frame(c(Catch_f, Index_f, ML_f)) # combine fleet data
names(fleet_data) = paste0(names(fleet_data),"_s",stock,"_f",fleet) # label correctly
LH_sel_data = data.frame(K_s1 = Some_data$K[stock], # combine life history and selectivity info
M_K_s1 = Some_data$M[stock]/Some_data$K[stock], # ratio of M to K
maxa_s1 = Some_data$maxa[stock], # age at 5% natural survival
L5_L50_s1_f1 = Some_data$L5[stock,fleet]/Some_data$L50[stock], # shortest length at 5% selectivity relative to L50
LFS_L50_s1_f1 = Some_data$LFS[stock,fleet]/Some_data$L50[stock]) # length at 95% selectivity relative to L50
input2 = list(data=cbind(LH_sel_data, fleet_data)) # Combine inputs into a single input object
Now that we have our dataset, extracted from three time series (Catch, a CPUE index and Mean Length) in addition to selectivity and life history parameters, we can train and use the indicator:
my_indicator2 = train_ind(input2) # train indicator, standardize input data
And then get an estimate of stock status:
pred_ind(my_indicator2) # estimate stock status from trained indicator and submitted dataset
The ETest package comes with a number of real fishery datasets loaded. You can list these using the R function data():
data(package="ETest")
There is an object TD which is the full training dataset. You can always look at that if you want to check the magnitude and range of data inputs used for training the neural networks.
The other objects are labelled _io, _data and _retro.
_io objects such as Shark_1_io are list objects that contain all of the inputs and outputs to a Stock Synthesis 3 assessment.
_data objects are list objects that contain ETest formatted features obtained from those _io stock synthesis runs (position 1, ‘inputs’) and also the stock synthesis estimated stock status (position 2, ‘Brel’).
_retro objects are a list of _data objects npeels long which strip back the data from the stock synthesis runs to allow retrospective behavior of indicators to be evaluated.
Using these objects it is simple to conduct indicator training and prediction, including retrospective analysis:
# Single stock status estimate using data to most recent year:
Shark_1_ind = train_ind(Shark_1_data)
Shark_1_pred = pred_ind(Shark_1_ind)
hist(Shark_1_pred)
# Retropsective stock status estimate using npeels of the SS3 data:
Shark_1_ind_retro = train_ind(Shark_1_retro)
Shark_1_pred_retro = pred_ind(Shark_1_ind_retro)
To obtain all relevant outputs for a current Stock Synthesis model you will a directory where an assessment has been run and you will need to install the latest version of the r4ss package.
wm_io = all_ss3("C:/myassessments/ATL_white_marlin_2025")
Once you have the assessment ripped into R, you need to pick what fleets and indices you wish to extract. You can use the ss_names() function to list the fleets / surveys. You then have to select the relevant fleets and corresponding cpue indices and then make your formatted EcoTest indicator features using SS_2_ET():
ss_names(wm_io)
wm_data = SS_2_ET(wm_io, Fnam = c("JPN_LL_N","US_PL_N"), Inam = c("Surv_JPN_LL_N","Surv_US_PL_N"))
The wm_data object now has features for three fleet data sets: the Japanese longline north the US pole and line north and all other fleets combined. Selectivities for all other fleets combined are weighted by historical average catches.
The function SS_2_ET() samples from time series and parameters to provide a stochastic feature dataset in order to obtain variance in stock status estimates.
You can now run the indicator for that dataset:
wm_ind = train_ind(wm_data)
wm_pred = pred_ind(wm_ind)
or alternatively you can do the same steps but conduct a retrospective analysis using the function SS_2_ET_Retro():
wm_retro = SS_2_ET_Retro(wm_io, Fnam = c("JPN_LL_N","US_PL_N"), Inam = c("Surv_JPN_LL_N","Surv_US_PL_N"),npeels=8)
wm_ind_retro = train_ind(wm_retro)
wm_pred_retro = pred_ind(wm_ind_retro)
In general, fisheries stock assessment has failed to incorporate information across multiple species in the estimation of stock status. This is a missed opportunity.
For example if the denominator of CPUE indcies of abundance (effort) is comparable in trend among two species, their catch ratio informs their relative depletion (e.g. if the catch ratio of stock 1 : stock 2 has declined by 50%, then this infers that stock 1 is at half the vulnerable stock size as stock 2 (assuming you standardized your data correctly).
This means that depletion on one stock may be informed by data informing depletion on the other stock. As the number of stocks increases (that adhere to this assumption of approximately comparable effort trends) an increasibly narrow set of depletion conditions may prevail. To demonstrate this lets see how well only catch data can inform stock status across three species:
fleet = 1 # ... one fleet.
Catch_f1 = ts_features(Some_data$Catch[1,fleet,],"C")
Catch_f2 = ts_features(Some_data$Catch[2,fleet,],"C")
Catch_f3 = ts_features(Some_data$Catch[3,fleet,],"C")
cvec = c(unlist(Catch_f1),unlist(Catch_f2),unlist(Catch_f3))
msdat = data.frame(matrix(cvec,1))
names(msdat) = paste0(names(cvec),"_s",rep(1:3,each=5),"_f1")
input = list(dat=msdat)
catch_ind = train_ind(input)
Since there is nothing providing an indication of absolute depletion the indicator based on catches alone performs very poorly.
fleet = 1 # ... one fleet.
Index_s1 = ts_features(Some_data$Index[1,fleet,],"I")
Index_s2 = ts_features(Some_data$Index[2,fleet,],"I")
Index_s3 = ts_features(Some_data$Index[3,fleet,],"I")
Index_rels = data.frame(I_rel_s1_f1 = Index_s1$I_rel,
I_rel_s2_f1 = Index_s2$I_rel,
I_rel_s3_f1 = Index_s3$I_rel)
wIdat = list(dat = cbind(msdat, Index_rels))
catch_wI_ind = train_ind(wIdat)
Adding just the current index levels relative to historical means, tidied the indicator up somewhat.
Now lets find out how much of that was just the index levels:
catch_onlyI_ind = train_ind(list(dat=Index_rels))
As expected, the relative index levels were doing the majority (but not all) of the lifting.
To use EcoTest, you must format your input data as described in Table 1.
Table 1. Indicator Features (data types)
| Feature | Type | Description |
|---|---|---|
| K | Stock | von Bertalanffy growth parameter (annual) |
| M_K | Stock | The ratio of natural mortality rate to von B. K |
| maxa | Stock | Maximum age. The age corresponding to 5% natural survival (the age before which 95% of individuals have died from natural mortality) |
| L50_Linf | Stock | The ratio of length at 50% maturity (spawning fraction) to asymptotic length (von B. L-infinity) |
| L5 | Fleet | The shortest length at 5% selectivity phrased as a % of asymptotic length (L-infinity) |
| LFS | Fleet | The shortest length at 95% selectivity phrased as a % of asymptotic length (L-infinity) |
| VML | Fleet | The vulnerability at maximum length (‘dome-shapedness’) |
| I_rel | Data | The ratio of current index level to mean historical index level |
| I_g5 | Data | The slope in log index over the most recent 5 years |
| I_g10 | Data | The slope in log index over the most recent 10 years |
| I_g20 | Data | The slope in log index over the most recent 20 years |
| Isd1 | Data | Mean absolute residual error in a log CPUE index from a loess smoother with effective number of parameters of 40% index length |
| Isd2 | Data | As Isd1 but ENP of 20% |
| Isd3 | Data | As Isd1 but ENP of 10% |
| C_rel | Data | The ratio of current catches relative to mean historical levels |
| C_g5 | Data | The slope in log catches over the most recent 5 years |
| C_g10 | Data | The slope in log catches over the most recent 10 years |
| C_g20 | Data | The slope in log catches over the most recent 20 years |
| Csd1 | Data | Standard deviation in residual error around log catches from a loess smoother with ENP 20% catch series length |
| CF | Data | The fraction of total historical catches (taken by this fleet). |
| ML_rel | Data | The ratio of current mean length in catches relative to historical average mean length in catches. Mean length is phrased as a % of asymptotic length (L-infinity) |
| ML_g5 | Data | The slope in log of mean length of catches over the most recent 5 years |
| ML_g10 | Data | The slope in log of mean length of catches over the most recent 10 years |
| ML_g20 | Data | The slope in log mean length of catches over the most recent 20 years |
| ML_Linf | Data | The ratio of current mean length in catches relative to asymptotic length (von B. L-infinity) |
| ML_L50 | Data | The ratio of current mean length in cathes to length at 50% maturity (spawning fraction) |
| MA_rel | Data | The ratio of current mean age in catch relative to mean historical levels |
| MA_g5 | Data | The slope in log mean age in catch over the most recent 5 years |
| MA_g10 | Data | The slope in log mean age in catch over the most recent 10 years |
| MA_g20 | Data | The slope in log mean age in catch over the most recent 20 years |
| MV_rel | Data | The standard deviation in observed caught lengths in the current year relative to the historical average. |
| MV_g5 | Data | The slope in log standard deviation in catches over the most recent 5 years |
| MV_g10 | Data | The slope in log standard deviation in catches over the most recent 10 years |
| MV_g20 | Data | The slope in log standard deviation in catches over the most recent 20 years |
| FM_rel | Data | The ratio of current fraction mature fish in catches relative to historical mean fraction of mature fish in catches |
| MV_g5 | Data | The slope in log of fraction mature fish in catches over the most recent 5 years |
| MV_g10 | Data | The slope in log of fraction mature fish in catches over the most recent 10 years |
| MV_g20 | Data | The slope in log fraction mature fish in catches over the most recent 20 years |
| CR | Data | The current catch ratio (first stock divided by second stock). R(y) = C(1,y) / C(2,y). CR is R from the most recent year, R(ny) |
| CR_mu | Data | The catch ratio (first stock divided by second stock) over the first 20 years R[1:20] |
| CR_rel | Data | CR / CR_mu |
| CR_s5 | Data | The slope in log CR over the most recent 5 years |
| CR_s10 | Data | The slope in log CR over the most recent 10 years |
| CC20 | Data | Correlation of the in residual fit of a loess smoother to log catches with ENP = 20% among stocks over the most recent 20 years. |
| CC40 | Data | As CC20 but over historical years between 21 and 40 years ago. |
| CC60 | Data | As CC20 but over historical years between 41 and 60 years ago. |
Any stock-specific data_input has an ‘s’ code. Any fleet specific input has a stock and a fleet ‘f’ code. For example, somatic growth K for stock 1 is codified K_s1. Catches relative to historical average Crel for stock 2, fleet 1 is Crel_s2_f1. You can always list the full set of names of all the possible features by getting the names of the training dataset:
names(TD)
## [1] "Res" "K_s1" "M_K_s1" "maxa_s1"
## [5] "L50_Linf_s1" "K_s2" "M_K_s2" "maxa_s2"
## [9] "L50_Linf_s2" "K_s3" "M_K_s3" "maxa_s3"
## [13] "L50_Linf_s3" "I_rel_s1_f1" "I_g5_s1_f1" "I_g10_s1_f1"
## [17] "I_g20_s1_f1" "I_g40_s1_f1" "Isd1_s1_f1" "Isd2_s1_f1"
## [21] "Isd3_s1_f1" "C_rel_s1_f1" "C_g5_s1_f1" "C_g10_s1_f1"
## [25] "C_g20_s1_f1" "C_g40_s1_f1" "Csd_s1_f1" "CF_s1_f1"
## [29] "ML_cur_s1_f1" "ML_rel_s1_f1" "ML_g5_s1_f1" "ML_g10_s1_f1"
## [33] "ML_g20_s1_f1" "ML_g40_s1_f1" "ML_Linf_s1_f1" "MV_cur_s1_f1"
## [37] "MV_rel_s1_f1" "MV_g5_s1_f1" "MV_g10_s1_f1" "MV_g20_s1_f1"
## [41] "MV_g40_s1_f1" "FM_cur_s1_f1" "FM_rel_s1_f1" "FM_g5_s1_f1"
## [45] "FM_g10_s1_f1" "FM_g20_s1_f1" "FM_g40_s1_f1" "MA_cur_s1_f1"
## [49] "MA_rel_s1_f1" "MA_g5_s1_f1" "MA_g10_s1_f1" "MA_g20_s1_f1"
## [53] "MA_g40_s1_f1" "L5_L50_s1_f1" "LFS_L50_s1_f1" "VML_s1_f1"
## [57] "I_rel_s1_f2" "I_g5_s1_f2" "I_g10_s1_f2" "I_g20_s1_f2"
## [61] "I_g40_s1_f2" "Isd1_s1_f2" "Isd2_s1_f2" "Isd3_s1_f2"
## [65] "C_rel_s1_f2" "C_g5_s1_f2" "C_g10_s1_f2" "C_g20_s1_f2"
## [69] "C_g40_s1_f2" "Csd_s1_f2" "CF_s1_f2" "ML_cur_s1_f2"
## [73] "ML_rel_s1_f2" "ML_g5_s1_f2" "ML_g10_s1_f2" "ML_g20_s1_f2"
## [77] "ML_g40_s1_f2" "ML_Linf_s1_f2" "MV_cur_s1_f2" "MV_rel_s1_f2"
## [81] "MV_g5_s1_f2" "MV_g10_s1_f2" "MV_g20_s1_f2" "MV_g40_s1_f2"
## [85] "FM_cur_s1_f2" "FM_rel_s1_f2" "FM_g5_s1_f2" "FM_g10_s1_f2"
## [89] "FM_g20_s1_f2" "FM_g40_s1_f2" "MA_cur_s1_f2" "MA_rel_s1_f2"
## [93] "MA_g5_s1_f2" "MA_g10_s1_f2" "MA_g20_s1_f2" "MA_g40_s1_f2"
## [97] "L5_L50_s1_f2" "LFS_L50_s1_f2" "VML_s1_f2" "I_rel_s1_f3"
## [101] "I_g5_s1_f3" "I_g10_s1_f3" "I_g20_s1_f3" "I_g40_s1_f3"
## [105] "Isd1_s1_f3" "Isd2_s1_f3" "Isd3_s1_f3" "C_rel_s1_f3"
## [109] "C_g5_s1_f3" "C_g10_s1_f3" "C_g20_s1_f3" "C_g40_s1_f3"
## [113] "Csd_s1_f3" "CF_s1_f3" "ML_cur_s1_f3" "ML_rel_s1_f3"
## [117] "ML_g5_s1_f3" "ML_g10_s1_f3" "ML_g20_s1_f3" "ML_g40_s1_f3"
## [121] "ML_Linf_s1_f3" "MV_cur_s1_f3" "MV_rel_s1_f3" "MV_g5_s1_f3"
## [125] "MV_g10_s1_f3" "MV_g20_s1_f3" "MV_g40_s1_f3" "FM_cur_s1_f3"
## [129] "FM_rel_s1_f3" "FM_g5_s1_f3" "FM_g10_s1_f3" "FM_g20_s1_f3"
## [133] "FM_g40_s1_f3" "MA_cur_s1_f3" "MA_rel_s1_f3" "MA_g5_s1_f3"
## [137] "MA_g10_s1_f3" "MA_g20_s1_f3" "MA_g40_s1_f3" "L5_L50_s1_f3"
## [141] "LFS_L50_s1_f3" "VML_s1_f3" "I_rel_s2_f1" "I_g5_s2_f1"
## [145] "I_g10_s2_f1" "I_g20_s2_f1" "I_g40_s2_f1" "Isd1_s2_f1"
## [149] "Isd2_s2_f1" "Isd3_s2_f1" "C_rel_s2_f1" "C_g5_s2_f1"
## [153] "C_g10_s2_f1" "C_g20_s2_f1" "C_g40_s2_f1" "Csd_s2_f1"
## [157] "CF_s2_f1" "ML_cur_s2_f1" "ML_rel_s2_f1" "ML_g5_s2_f1"
## [161] "ML_g10_s2_f1" "ML_g20_s2_f1" "ML_g40_s2_f1" "ML_Linf_s2_f1"
## [165] "MV_cur_s2_f1" "MV_rel_s2_f1" "MV_g5_s2_f1" "MV_g10_s2_f1"
## [169] "MV_g20_s2_f1" "MV_g40_s2_f1" "FM_cur_s2_f1" "FM_rel_s2_f1"
## [173] "FM_g5_s2_f1" "FM_g10_s2_f1" "FM_g20_s2_f1" "FM_g40_s2_f1"
## [177] "MA_cur_s2_f1" "MA_rel_s2_f1" "MA_g5_s2_f1" "MA_g10_s2_f1"
## [181] "MA_g20_s2_f1" "MA_g40_s2_f1" "L5_L50_s2_f1" "LFS_L50_s2_f1"
## [185] "VML_s2_f1" "I_rel_s2_f2" "I_g5_s2_f2" "I_g10_s2_f2"
## [189] "I_g20_s2_f2" "I_g40_s2_f2" "Isd1_s2_f2" "Isd2_s2_f2"
## [193] "Isd3_s2_f2" "C_rel_s2_f2" "C_g5_s2_f2" "C_g10_s2_f2"
## [197] "C_g20_s2_f2" "C_g40_s2_f2" "Csd_s2_f2" "CF_s2_f2"
## [201] "ML_cur_s2_f2" "ML_rel_s2_f2" "ML_g5_s2_f2" "ML_g10_s2_f2"
## [205] "ML_g20_s2_f2" "ML_g40_s2_f2" "ML_Linf_s2_f2" "MV_cur_s2_f2"
## [209] "MV_rel_s2_f2" "MV_g5_s2_f2" "MV_g10_s2_f2" "MV_g20_s2_f2"
## [213] "MV_g40_s2_f2" "FM_cur_s2_f2" "FM_rel_s2_f2" "FM_g5_s2_f2"
## [217] "FM_g10_s2_f2" "FM_g20_s2_f2" "FM_g40_s2_f2" "MA_cur_s2_f2"
## [221] "MA_rel_s2_f2" "MA_g5_s2_f2" "MA_g10_s2_f2" "MA_g20_s2_f2"
## [225] "MA_g40_s2_f2" "L5_L50_s2_f2" "LFS_L50_s2_f2" "VML_s2_f2"
## [229] "I_rel_s2_f3" "I_g5_s2_f3" "I_g10_s2_f3" "I_g20_s2_f3"
## [233] "I_g40_s2_f3" "Isd1_s2_f3" "Isd2_s2_f3" "Isd3_s2_f3"
## [237] "C_rel_s2_f3" "C_g5_s2_f3" "C_g10_s2_f3" "C_g20_s2_f3"
## [241] "C_g40_s2_f3" "Csd_s2_f3" "CF_s2_f3" "ML_cur_s2_f3"
## [245] "ML_rel_s2_f3" "ML_g5_s2_f3" "ML_g10_s2_f3" "ML_g20_s2_f3"
## [249] "ML_g40_s2_f3" "ML_Linf_s2_f3" "MV_cur_s2_f3" "MV_rel_s2_f3"
## [253] "MV_g5_s2_f3" "MV_g10_s2_f3" "MV_g20_s2_f3" "MV_g40_s2_f3"
## [257] "FM_cur_s2_f3" "FM_rel_s2_f3" "FM_g5_s2_f3" "FM_g10_s2_f3"
## [261] "FM_g20_s2_f3" "FM_g40_s2_f3" "MA_cur_s2_f3" "MA_rel_s2_f3"
## [265] "MA_g5_s2_f3" "MA_g10_s2_f3" "MA_g20_s2_f3" "MA_g40_s2_f3"
## [269] "L5_L50_s2_f3" "LFS_L50_s2_f3" "VML_s2_f3" "I_rel_s3_f1"
## [273] "I_g5_s3_f1" "I_g10_s3_f1" "I_g20_s3_f1" "I_g40_s3_f1"
## [277] "Isd1_s3_f1" "Isd2_s3_f1" "Isd3_s3_f1" "C_rel_s3_f1"
## [281] "C_g5_s3_f1" "C_g10_s3_f1" "C_g20_s3_f1" "C_g40_s3_f1"
## [285] "Csd_s3_f1" "CF_s3_f1" "ML_cur_s3_f1" "ML_rel_s3_f1"
## [289] "ML_g5_s3_f1" "ML_g10_s3_f1" "ML_g20_s3_f1" "ML_g40_s3_f1"
## [293] "ML_Linf_s3_f1" "MV_cur_s3_f1" "MV_rel_s3_f1" "MV_g5_s3_f1"
## [297] "MV_g10_s3_f1" "MV_g20_s3_f1" "MV_g40_s3_f1" "FM_cur_s3_f1"
## [301] "FM_rel_s3_f1" "FM_g5_s3_f1" "FM_g10_s3_f1" "FM_g20_s3_f1"
## [305] "FM_g40_s3_f1" "MA_cur_s3_f1" "MA_rel_s3_f1" "MA_g5_s3_f1"
## [309] "MA_g10_s3_f1" "MA_g20_s3_f1" "MA_g40_s3_f1" "L5_L50_s3_f1"
## [313] "LFS_L50_s3_f1" "VML_s3_f1" "I_rel_s3_f2" "I_g5_s3_f2"
## [317] "I_g10_s3_f2" "I_g20_s3_f2" "I_g40_s3_f2" "Isd1_s3_f2"
## [321] "Isd2_s3_f2" "Isd3_s3_f2" "C_rel_s3_f2" "C_g5_s3_f2"
## [325] "C_g10_s3_f2" "C_g20_s3_f2" "C_g40_s3_f2" "Csd_s3_f2"
## [329] "CF_s3_f2" "ML_cur_s3_f2" "ML_rel_s3_f2" "ML_g5_s3_f2"
## [333] "ML_g10_s3_f2" "ML_g20_s3_f2" "ML_g40_s3_f2" "ML_Linf_s3_f2"
## [337] "MV_cur_s3_f2" "MV_rel_s3_f2" "MV_g5_s3_f2" "MV_g10_s3_f2"
## [341] "MV_g20_s3_f2" "MV_g40_s3_f2" "FM_cur_s3_f2" "FM_rel_s3_f2"
## [345] "FM_g5_s3_f2" "FM_g10_s3_f2" "FM_g20_s3_f2" "FM_g40_s3_f2"
## [349] "MA_cur_s3_f2" "MA_rel_s3_f2" "MA_g5_s3_f2" "MA_g10_s3_f2"
## [353] "MA_g20_s3_f2" "MA_g40_s3_f2" "L5_L50_s3_f2" "LFS_L50_s3_f2"
## [357] "VML_s3_f2" "I_rel_s3_f3" "I_g5_s3_f3" "I_g10_s3_f3"
## [361] "I_g20_s3_f3" "I_g40_s3_f3" "Isd1_s3_f3" "Isd2_s3_f3"
## [365] "Isd3_s3_f3" "C_rel_s3_f3" "C_g5_s3_f3" "C_g10_s3_f3"
## [369] "C_g20_s3_f3" "C_g40_s3_f3" "Csd_s3_f3" "CF_s3_f3"
## [373] "ML_cur_s3_f3" "ML_rel_s3_f3" "ML_g5_s3_f3" "ML_g10_s3_f3"
## [377] "ML_g20_s3_f3" "ML_g40_s3_f3" "ML_Linf_s3_f3" "MV_cur_s3_f3"
## [381] "MV_rel_s3_f3" "MV_g5_s3_f3" "MV_g10_s3_f3" "MV_g20_s3_f3"
## [385] "MV_g40_s3_f3" "FM_cur_s3_f3" "FM_rel_s3_f3" "FM_g5_s3_f3"
## [389] "FM_g10_s3_f3" "FM_g20_s3_f3" "FM_g40_s3_f3" "MA_cur_s3_f3"
## [393] "MA_rel_s3_f3" "MA_g5_s3_f3" "MA_g10_s3_f3" "MA_g20_s3_f3"
## [397] "MA_g40_s3_f3" "L5_L50_s3_f3" "LFS_L50_s3_f3" "VML_s3_f3"
## [401] "CR_s1_s2_f1" "CR_s1_s3_f1" "CR_s2_s3_f1" "CR_mu_s1_s2_f1"
## [405] "CR_mu_s1_s3_f1" "CR_mu_s2_s3_f1" "CR_rel_s1_s2_f1" "CR_rel_s1_s3_f1"
## [409] "CR_rel_s2_s3_f1" "CR_g5_s1_s2_f1" "CR_g5_s1_s3_f1" "CR_g5_s2_s3_f1"
## [413] "CR_g10_s1_s2_f1" "CR_g10_s1_s3_f1" "CR_g10_s2_s3_f1" "CR_g20_s1_s2_f1"
## [417] "CR_g20_s1_s3_f1" "CR_g20_s2_s3_f1" "CC20_s1_s2_f1" "CC20_s1_s3_f1"
## [421] "CC20_s2_s3_f1" "CC40_s1_s2_f1" "CC40_s1_s3_f1" "CC40_s2_s3_f1"
## [425] "CC60_s1_s2_f1" "CC60_s1_s3_f1" "CC60_s2_s3_f1" "CR_s1_s2_f2"
## [429] "CR_s1_s3_f2" "CR_s2_s3_f2" "CR_mu_s1_s2_f2" "CR_mu_s1_s3_f2"
## [433] "CR_mu_s2_s3_f2" "CR_rel_s1_s2_f2" "CR_rel_s1_s3_f2" "CR_rel_s2_s3_f2"
## [437] "CR_g5_s1_s2_f2" "CR_g5_s1_s3_f2" "CR_g5_s2_s3_f2" "CR_g10_s1_s2_f2"
## [441] "CR_g10_s1_s3_f2" "CR_g10_s2_s3_f2" "CR_g20_s1_s2_f2" "CR_g20_s1_s3_f2"
## [445] "CR_g20_s2_s3_f2" "CC20_s1_s2_f2" "CC20_s1_s3_f2" "CC20_s2_s3_f2"
## [449] "CC40_s1_s2_f2" "CC40_s1_s3_f2" "CC40_s2_s3_f2" "CC60_s1_s2_f2"
## [453] "CC60_s1_s3_f2" "CC60_s2_s3_f2" "CR_s1_s2_f3" "CR_s1_s3_f3"
## [457] "CR_s2_s3_f3" "CR_mu_s1_s2_f3" "CR_mu_s1_s3_f3" "CR_mu_s2_s3_f3"
## [461] "CR_rel_s1_s2_f3" "CR_rel_s1_s3_f3" "CR_rel_s2_s3_f3" "CR_g5_s1_s2_f3"
## [465] "CR_g5_s1_s3_f3" "CR_g5_s2_s3_f3" "CR_g10_s1_s2_f3" "CR_g10_s1_s3_f3"
## [469] "CR_g10_s2_s3_f3" "CR_g20_s1_s2_f3" "CR_g20_s1_s3_f3" "CR_g20_s2_s3_f3"
## [473] "CC20_s1_s2_f3" "CC20_s1_s3_f3" "CC20_s2_s3_f3" "CC40_s1_s2_f3"
## [477] "CC40_s1_s3_f3" "CC40_s2_s3_f3" "CC60_s1_s2_f3" "CC60_s1_s3_f3"
## [481] "CC60_s2_s3_f3"
Zhou’s intro to ANN and machine learning
Tensorflow: Platform for machine learning
Nvidia CUDA Toolkit: GPU accelerated applications
Carruthers, T.R. 2018. A multispecies catch-ratio estimator of relative stock depletion. Fisheries Research. 197: 25-33, https://doi.org/10.1016/j.fishres.2017.09.017.
Huynh, Q., 2025. Rapid Conditioning model. Available from https://openmse.com/tutorial-rcm/
Juan-Jordá, M.J., Murua, H., Arrizabalaga, H., Dulvy, N.K., and Restrepo, V. 2018. Report card on ecosystem-based fisheries management in tuna regional fisheries management organizations. Fish Fish. 19: 321-339.
EcoTest is funded by ICCAT as part of a commitment to the Global Environmental Facility (GEF) Common Oceans ABNJ Tuna Project Fund.
Many thanks to The Ocean Foundation and the Pew Charitable Trusts for funding Phases I and II of the EcoTest framework.
Many thanks to the ICCAT Ecosystem working group for comments on earlier versions of EcoTest: Nathan Taylor, Alex Hanke, Sachiko Tsuji, Guillermo Diaz, Diego Alvarez, Laurie Kell, Maria Juan Jorda, Andres Domingo.
Blue Matter: Tom Carruthers, Adrian Hordyk, Quang Huynh
OpenMSE was developed with support from the Natural Resources Defense Council (NRDC), the Gordon and Betty Moore Foundation, the Packard Foundation, the Marine Stewardship Council, Fisheries and Oceans Canada (DFO), the U.S. National Oceanic and Atmospheric Administration, the International Commission for the Conservation of Atlantic Tunas (ICCAT) and The Ocean Foundation.
OpenMSE continues to be developed with the support of the The Ocean Foundation.