ART Options Example

Source code notebook compat Author Update time

Overview

The AdaptiveResonance.jl package has several ways of handling options for ART modules. These methods are meant to give maximum flexibility to the user for sharing and interpreting options, which themselves vary between each module.

Note

For more info on options in ART modules, see the guide in the docs on ART options.

ART Options

To get a feel for the ART options system, we will inspect different options and their instantiation methods.

Inspection

First, we load AdaptiveResonance:

using AdaptiveResonance

Every ART module has a default constructor, which can be instantiated in the usual way:

# Create a FuzzyART module with default options
my_fuzzyart = FuzzyART()
typeof(my_fuzzyart)
FuzzyART

Within every ART module is a Parameters.jl struct named opts containing the options for the module

# Check the FuzzyART options
my_fuzzyart.opts
opts_FuzzyART
  rho: Float64 0.6
  alpha: Float64 0.001
  beta: Float64 1.0
  gamma: Float64 3.0
  gamma_ref: Float64 1.0
  max_epoch: Int64 1
  display: Bool false
  gamma_normalization: Bool false
  uncommitted: Bool false
  activation: Symbol basic_activation
  match: Symbol basic_match
  update: Symbol basic_update

Note that the options here have the type opts_FuzzyART. This nomenclature is used throughout the module to indicate an options type associated with an ART module. For example, the options for a DDVFA module are opts_DDVFA:

# Create a DDVFA module and check the type of the options
my_ddvfa = DDVFA()
typeof(my_ddvfa.opts)
opts_DDVFA

In fact, we can create an instance of these options with a default constructor:

# Create a separate options struct
my_fuzzyart_opts = opts_FuzzyART()
opts_FuzzyART
  rho: Float64 0.6
  alpha: Float64 0.001
  beta: Float64 1.0
  gamma: Float64 3.0
  gamma_ref: Float64 1.0
  max_epoch: Int64 1
  display: Bool false
  gamma_normalization: Bool false
  uncommitted: Bool false
  activation: Symbol basic_activation
  match: Symbol basic_match
  update: Symbol basic_update

In addition to the default constructor, we can construct ART modules by instantiating these options and passing them to the module during construction:

# Instantiate an ART module by passing our options
my_fuzzyart = FuzzyART(my_fuzzyart_opts)
my_other_fuzzyart = FuzzyART(my_fuzzyart_opts)
FuzzyART(opts_FuzzyART
  rho: Float64 0.6
  alpha: Float64 0.001
  beta: Float64 1.0
  gamma: Float64 3.0
  gamma_ref: Float64 1.0
  max_epoch: Int64 1
  display: Bool false
  gamma_normalization: Bool false
  uncommitted: Bool false
  activation: Symbol basic_activation
  match: Symbol basic_match
  update: Symbol basic_update
, DataConfig(false, Float64[], Float64[], 0, 0), 0.0, Int64[], Float64[], Float64[], 0×0 ElasticArrays.ElasticMatrix{Float64, Vector{Float64}}, Int64[], 0, 0, Dict{String, Any}("bmu" => 0, "mismatch" => false, "M" => 0.0, "T" => 0.0))

Specifying Options

Now to the good stuff: because of the behavior of the Parameters.jl type, each option has a default value that we can modify during instantiation with keyword arguments:

# Change some of the default FuzzyART options
my_fuzzyart_opts = opts_FuzzyART(
    rho=0.6,
    gamma_normalization=true
)
my_fuzzyart = FuzzyART(my_fuzzyart_opts)
FuzzyART(opts_FuzzyART
  rho: Float64 0.6
  alpha: Float64 0.001
  beta: Float64 1.0
  gamma: Float64 3.0
  gamma_ref: Float64 1.0
  max_epoch: Int64 1
  display: Bool false
  gamma_normalization: Bool true
  uncommitted: Bool false
  activation: Symbol gamma_activation
  match: Symbol gamma_match
  update: Symbol basic_update
, DataConfig(false, Float64[], Float64[], 0, 0), 0.0, Int64[], Float64[], Float64[], 0×0 ElasticArrays.ElasticMatrix{Float64, Vector{Float64}}, Int64[], 0, 0, Dict{String, Any}("bmu" => 0, "mismatch" => false, "M" => 0.0, "T" => 0.0))

As some syntactic sugar, we can pass these keyword arguments directly to the module during instantiation if we have no need to share option structs:

# Pass these keyword arguments to the module directly
my_fuzzyart = FuzzyART(
    rho=0.6,
    gamma_normalization=true
)
FuzzyART(opts_FuzzyART
  rho: Float64 0.6
  alpha: Float64 0.001
  beta: Float64 1.0
  gamma: Float64 3.0
  gamma_ref: Float64 1.0
  max_epoch: Int64 1
  display: Bool false
  gamma_normalization: Bool true
  uncommitted: Bool false
  activation: Symbol gamma_activation
  match: Symbol gamma_match
  update: Symbol basic_update
, DataConfig(false, Float64[], Float64[], 0, 0), 0.0, Int64[], Float64[], Float64[], 0×0 ElasticArrays.ElasticMatrix{Float64, Vector{Float64}}, Int64[], 0, 0, Dict{String, Any}("bmu" => 0, "mismatch" => false, "M" => 0.0, "T" => 0.0))

Before training, we can also instantiate the model and alter the options afterward:

my_fuzzyart = FuzzyART()
my_fuzzyart.opts.rho=0.6
0.6
Note

All ART modules are designed to use this options struct internally when the parameters are needed. It is possible to change these parameters in the middle of training and evaluation, but some algorithmic instability may occur.

Comparison

To see the effect that changing these parameters has on the modules, we can train and test them side-by-side.

We begin with importing AdaptiveResonance for the ART modules and MLDatasets for some data utilities.

using MLDatasets        # Iris dataset
using DataFrames        # DataFrames, necessary for MLDatasets.Iris()
using MLDataUtils       # Shuffling and splitting
using Printf            # Formatted number printing
using MultivariateStats # Principal component analysis (PCA)
using Plots             # Plotting frontend
gr()                    # Use the default GR backend explicitly
Plots.GRBackend()

We will download the Iris dataset for its small size and benchmark use for clustering algorithms.

# Get the iris dataset
iris = Iris(as_df=false)
# Manipulate the features and labels into a matrix of features and a vector of labels
features, labels = iris.features, iris.targets
([5.1 4.9 4.7 4.6 5.0 5.4 4.6 5.0 4.4 4.9 5.4 4.8 4.8 4.3 5.8 5.7 5.4 5.1 5.7 5.1 5.4 5.1 4.6 5.1 4.8 5.0 5.0 5.2 5.2 4.7 4.8 5.4 5.2 5.5 4.9 5.0 5.5 4.9 4.4 5.1 5.0 4.5 4.4 5.0 5.1 4.8 5.1 4.6 5.3 5.0 7.0 6.4 6.9 5.5 6.5 5.7 6.3 4.9 6.6 5.2 5.0 5.9 6.0 6.1 5.6 6.7 5.6 5.8 6.2 5.6 5.9 6.1 6.3 6.1 6.4 6.6 6.8 6.7 6.0 5.7 5.5 5.5 5.8 6.0 5.4 6.0 6.7 6.3 5.6 5.5 5.5 6.1 5.8 5.0 5.6 5.7 5.7 6.2 5.1 5.7 6.3 5.8 7.1 6.3 6.5 7.6 4.9 7.3 6.7 7.2 6.5 6.4 6.8 5.7 5.8 6.4 6.5 7.7 7.7 6.0 6.9 5.6 7.7 6.3 6.7 7.2 6.2 6.1 6.4 7.2 7.4 7.9 6.4 6.3 6.1 7.7 6.3 6.4 6.0 6.9 6.7 6.9 5.8 6.8 6.7 6.7 6.3 6.5 6.2 5.9; 3.5 3.0 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 3.7 3.4 3.0 3.0 4.0 4.4 3.9 3.5 3.8 3.8 3.4 3.7 3.6 3.3 3.4 3.0 3.4 3.5 3.4 3.2 3.1 3.4 4.1 4.2 3.1 3.2 3.5 3.1 3.0 3.4 3.5 2.3 3.2 3.5 3.8 3.0 3.8 3.2 3.7 3.3 3.2 3.2 3.1 2.3 2.8 2.8 3.3 2.4 2.9 2.7 2.0 3.0 2.2 2.9 2.9 3.1 3.0 2.7 2.2 2.5 3.2 2.8 2.5 2.8 2.9 3.0 2.8 3.0 2.9 2.6 2.4 2.4 2.7 2.7 3.0 3.4 3.1 2.3 3.0 2.5 2.6 3.0 2.6 2.3 2.7 3.0 2.9 2.9 2.5 2.8 3.3 2.7 3.0 2.9 3.0 3.0 2.5 2.9 2.5 3.6 3.2 2.7 3.0 2.5 2.8 3.2 3.0 3.8 2.6 2.2 3.2 2.8 2.8 2.7 3.3 3.2 2.8 3.0 2.8 3.0 2.8 3.8 2.8 2.8 2.6 3.0 3.4 3.1 3.0 3.1 3.1 3.1 2.7 3.2 3.3 3.0 2.5 3.0 3.4 3.0; 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 1.5 1.6 1.4 1.1 1.2 1.5 1.3 1.4 1.7 1.5 1.7 1.5 1.0 1.7 1.9 1.6 1.6 1.5 1.4 1.6 1.6 1.5 1.5 1.4 1.5 1.2 1.3 1.5 1.3 1.5 1.3 1.3 1.3 1.6 1.9 1.4 1.6 1.4 1.5 1.4 4.7 4.5 4.9 4.0 4.6 4.5 4.7 3.3 4.6 3.9 3.5 4.2 4.0 4.7 3.6 4.4 4.5 4.1 4.5 3.9 4.8 4.0 4.9 4.7 4.3 4.4 4.8 5.0 4.5 3.5 3.8 3.7 3.9 5.1 4.5 4.5 4.7 4.4 4.1 4.0 4.4 4.6 4.0 3.3 4.2 4.2 4.2 4.3 3.0 4.1 6.0 5.1 5.9 5.6 5.8 6.6 4.5 6.3 5.8 6.1 5.1 5.3 5.5 5.0 5.1 5.3 5.5 6.7 6.9 5.0 5.7 4.9 6.7 4.9 5.7 6.0 4.8 4.9 5.6 5.8 6.1 6.4 5.6 5.1 5.6 6.1 5.6 5.5 4.8 5.4 5.6 5.1 5.1 5.9 5.7 5.2 5.0 5.2 5.4 5.1; 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 0.2 0.2 0.1 0.1 0.2 0.4 0.4 0.3 0.3 0.3 0.2 0.4 0.2 0.5 0.2 0.2 0.4 0.2 0.2 0.2 0.2 0.4 0.1 0.2 0.1 0.2 0.2 0.1 0.2 0.2 0.3 0.3 0.2 0.6 0.4 0.3 0.2 0.2 0.2 0.2 1.4 1.5 1.5 1.3 1.5 1.3 1.6 1.0 1.3 1.4 1.0 1.5 1.0 1.4 1.3 1.4 1.5 1.0 1.5 1.1 1.8 1.3 1.5 1.2 1.3 1.4 1.4 1.7 1.5 1.0 1.1 1.0 1.2 1.6 1.5 1.6 1.5 1.3 1.3 1.3 1.2 1.4 1.2 1.0 1.3 1.2 1.3 1.3 1.1 1.3 2.5 1.9 2.1 1.8 2.2 2.1 1.7 1.8 1.8 2.5 2.0 1.9 2.1 2.0 2.4 2.3 1.8 2.2 2.3 1.5 2.3 2.0 2.0 1.8 2.1 1.8 1.8 1.8 2.1 1.6 1.9 2.0 2.2 1.5 1.4 2.3 2.4 1.8 1.8 2.1 2.4 2.3 1.9 2.3 2.5 2.3 1.9 2.0 2.3 1.8], InlineStrings.String15["Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-versicolor" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica" "Iris-virginica"])

Because the MLDatasets package gives us Iris labels as strings, we will use the MLDataUtils.convertlabel method with the MLLabelUtils.LabelEnc.Indices type to get a list of integers representing each class:

labels = convertlabel(LabelEnc.Indices{Int}, vec(labels))
unique(labels)
3-element Vector{Int64}:
 1
 2
 3

Next, we will create a train/test split with the MLDataUtils.stratifiedobs utility:

(X_train, y_train), (X_test, y_test) = stratifiedobs((features, labels))
(([5.1 6.3 5.8 5.3 6.0 7.9 5.5 7.2 5.4 5.1 6.1 5.6 7.4 5.4 7.7 4.8 5.9 6.3 4.4 6.7 5.6 5.5 6.7 5.4 4.5 5.1 5.9 4.8 5.5 6.3 5.1 6.9 6.9 5.7 7.2 7.0 4.9 6.8 6.5 6.2 7.7 6.3 7.7 6.3 4.9 5.6 6.1 6.8 5.7 6.6 4.4 7.6 4.6 5.4 6.7 6.2 6.9 5.5 6.3 5.2 5.5 6.1 6.7 6.6 5.7 5.6 6.4 6.3 4.9 6.9 4.9 6.3 4.6 6.4 5.1 5.0 6.3 4.8 5.4 5.1 7.2 6.4 4.8 6.0 5.7 5.2 5.0 5.0 6.5 6.0 4.6 5.2 5.0 5.4 5.7 6.0 5.0 4.4 5.6 6.4 6.1 5.0 5.2 6.5 5.7; 3.5 3.3 2.6 3.7 2.7 3.8 2.6 3.0 3.9 3.4 3.0 2.5 2.8 3.4 3.0 3.0 3.0 2.5 3.0 3.3 3.0 2.3 3.0 3.9 2.3 3.8 3.0 3.4 2.4 2.5 3.5 3.1 3.1 2.5 3.2 3.2 3.1 3.2 3.0 2.9 2.8 2.8 2.6 2.7 3.0 3.0 2.9 2.8 3.0 3.0 2.9 3.0 3.2 3.7 3.1 3.4 3.1 3.5 2.9 3.4 2.4 2.6 2.5 2.9 2.8 2.8 2.7 2.3 3.1 3.2 2.5 3.3 3.6 3.1 2.5 3.3 3.4 3.1 3.4 3.8 3.6 3.2 3.4 2.9 3.8 2.7 3.6 3.4 2.8 2.2 3.1 3.5 3.5 3.0 2.8 3.4 3.0 3.2 2.9 2.8 3.0 2.0 4.1 3.2 2.6; 1.4 4.7 4.0 1.5 5.1 6.4 4.4 5.8 1.7 1.5 4.9 3.9 6.1 1.7 6.1 1.4 5.1 4.9 1.3 5.7 4.5 4.0 5.0 1.3 1.3 1.5 4.2 1.9 3.8 5.0 1.4 5.1 5.4 5.0 6.0 4.7 1.5 5.9 5.2 4.3 6.7 5.1 6.9 4.9 1.4 4.1 4.7 4.8 4.2 4.4 1.4 6.6 1.4 1.5 4.4 5.4 4.9 1.3 5.6 1.4 3.7 5.6 5.8 4.6 4.1 4.9 5.3 4.4 1.5 5.7 4.5 6.0 1.0 5.5 3.0 1.4 5.6 1.6 1.5 1.6 6.1 5.3 1.6 4.5 1.7 3.9 1.4 1.5 4.6 5.0 1.5 1.5 1.3 4.5 4.5 4.5 1.6 1.3 3.6 5.6 4.6 3.5 1.5 5.1 3.5; 0.3 1.6 1.2 0.2 1.6 2.0 1.2 1.6 0.4 0.2 1.8 1.1 1.9 0.2 2.3 0.1 1.8 1.5 0.2 2.1 1.5 1.3 1.7 0.4 0.3 0.3 1.5 0.2 1.1 1.9 0.2 2.3 2.1 2.0 1.8 1.4 0.1 2.3 2.0 1.3 2.0 1.5 2.3 1.8 0.2 1.3 1.4 1.4 1.2 1.4 0.2 2.1 0.2 0.2 1.4 2.3 1.5 0.2 1.8 0.2 1.0 1.4 1.8 1.3 1.3 2.0 1.9 1.3 0.1 2.3 1.7 2.5 0.2 1.8 1.1 0.2 2.4 0.2 0.4 0.2 2.5 2.3 0.2 1.5 0.3 1.4 0.2 0.2 1.5 1.5 0.2 0.2 0.3 1.5 1.3 1.6 0.2 0.2 1.3 2.2 1.4 1.0 0.1 2.0 1.0], [1, 2, 2, 1, 2, 3, 2, 3, 1, 1, 3, 2, 3, 1, 3, 1, 3, 2, 1, 3, 2, 2, 2, 1, 1, 1, 2, 1, 2, 3, 1, 3, 3, 3, 3, 2, 1, 3, 3, 2, 3, 3, 3, 3, 1, 2, 2, 2, 2, 2, 1, 3, 1, 1, 2, 3, 2, 1, 3, 1, 2, 3, 3, 2, 2, 3, 3, 2, 1, 3, 3, 3, 1, 3, 2, 1, 3, 1, 1, 1, 3, 3, 1, 2, 1, 2, 1, 1, 2, 3, 1, 1, 1, 2, 2, 2, 1, 1, 2, 3, 2, 2, 1, 3, 2]), ([5.8 5.0 5.8 6.0 5.1 5.5 6.5 5.7 4.9 6.5 5.0 6.7 5.9 6.8 4.9 5.7 6.1 4.7 5.5 5.6 7.7 6.0 6.2 6.1 5.8 6.4 6.7 4.7 4.6 5.1 4.8 6.2 6.7 5.0 5.8 6.4 5.8 5.8 6.4 7.3 5.0 5.1 7.1 6.7 4.3; 2.8 3.4 2.7 2.2 3.7 4.2 3.0 2.9 2.4 3.0 3.2 3.1 3.2 3.0 3.1 4.4 2.8 3.2 2.5 2.7 3.8 3.0 2.2 2.8 4.0 2.8 3.3 3.2 3.4 3.3 3.0 2.8 3.1 2.3 2.7 2.9 2.7 2.7 3.2 2.9 3.5 3.8 3.0 3.0 3.0; 5.1 1.6 4.1 4.0 1.5 1.4 5.5 4.2 3.3 5.8 1.2 4.7 4.8 5.5 1.5 1.5 4.7 1.6 4.0 4.2 6.7 4.8 4.5 4.0 1.2 5.6 5.7 1.3 1.4 1.7 1.4 4.8 5.6 3.3 3.9 4.3 5.1 5.1 4.5 6.3 1.6 1.9 5.9 5.2 1.1; 2.4 0.4 1.0 1.0 0.4 0.2 1.8 1.3 1.0 2.2 0.2 1.5 1.8 2.1 0.1 0.4 1.2 0.2 1.3 1.3 2.2 1.8 1.5 1.3 0.2 2.1 2.5 0.2 0.3 0.5 0.3 1.8 2.4 1.0 1.2 1.3 1.9 1.9 1.5 1.8 0.6 0.4 2.1 2.3 0.1], [3, 1, 2, 2, 1, 1, 3, 2, 2, 3, 1, 2, 2, 3, 1, 1, 2, 1, 2, 2, 3, 3, 2, 2, 1, 3, 3, 1, 1, 1, 1, 3, 3, 2, 2, 2, 3, 3, 2, 3, 1, 1, 3, 3, 1]))

Now we can create several FuzzyART modules with different options.

# Create two FuzzyARTs with different vigilance values and suppressing logging messages
rho_1 = 0.5
rho_2 = 0.7
my_fuzzyart_1 = FuzzyART(rho=rho_1, display=false)
my_fuzzyart_2 = FuzzyART(rho=rho_2, display=false)
FuzzyART(opts_FuzzyART
  rho: Float64 0.7
  alpha: Float64 0.001
  beta: Float64 1.0
  gamma: Float64 3.0
  gamma_ref: Float64 1.0
  max_epoch: Int64 1
  display: Bool false
  gamma_normalization: Bool false
  uncommitted: Bool false
  activation: Symbol basic_activation
  match: Symbol basic_match
  update: Symbol basic_update
, DataConfig(false, Float64[], Float64[], 0, 0), 0.0, Int64[], Float64[], Float64[], 0×0 ElasticArrays.ElasticMatrix{Float64, Vector{Float64}}, Int64[], 0, 0, Dict{String, Any}("bmu" => 0, "mismatch" => false, "M" => 0.0, "T" => 0.0))

Here, we will train these FuzzyART modules in simple supervised mode by passing the supervised labels as a keyword argument:

# Train in simple supervised mode by passing the labels as a keyword argument.
y_hat_train_1 = train!(my_fuzzyart_1, X_train, y=y_train)
y_hat_train_2 = train!(my_fuzzyart_2, X_train, y=y_train)
105-element Vector{Int64}:
 1
 2
 2
 1
 2
 3
 2
 3
 1
 1
 3
 2
 3
 1
 3
 1
 3
 2
 1
 3
 2
 2
 2
 1
 1
 1
 2
 1
 2
 3
 1
 3
 3
 3
 3
 2
 1
 3
 3
 2
 3
 3
 3
 3
 1
 2
 2
 2
 2
 2
 1
 3
 1
 1
 2
 3
 2
 1
 3
 1
 2
 3
 3
 2
 2
 3
 3
 2
 1
 3
 3
 3
 1
 3
 2
 1
 3
 1
 1
 1
 3
 3
 1
 2
 1
 2
 1
 1
 2
 3
 1
 1
 1
 2
 2
 2
 1
 1
 2
 3
 2
 2
 1
 3
 2

We then classify the test data with both modules:

y_hat_1 = AdaptiveResonance.classify(my_fuzzyart_1, X_test, get_bmu=true)
y_hat_2 = AdaptiveResonance.classify(my_fuzzyart_2, X_test, get_bmu=true)

# Check the shape and type of the output labels
println("FuzzyART 1 labels: ",  size(y_hat_1), " ", typeof(y_hat_1))
println("FuzzyART 2 labels: ",  size(y_hat_2), " ", typeof(y_hat_2))

# Calculate the performance on the test data
perf_test_1 = performance(y_hat_1, y_test)
perf_test_2 = performance(y_hat_2, y_test)

# Format each performance number for comparison
@printf "Testing performance rho=%.1f: %.4f\n" rho_1 perf_test_1
@printf "Testing performance rho=%.1f: %.4f\n" rho_2 perf_test_2
FuzzyART 1 labels: (45,) Vector{Int64}
FuzzyART 2 labels: (45,) Vector{Int64}
Testing performance rho=0.5: 0.9778
Testing performance rho=0.7: 0.9333

In addition to having different performances, we can see that there is a subsequent trade-off in the number of categories used:

# Print the number of categories for each vigilance parameter
@printf "Number of categories rho=%.1f: %i\n" rho_1 my_fuzzyart_1.n_categories
@printf "Number of categories rho=%.1f: %i\n" rho_2 my_fuzzyart_2.n_categories
Number of categories rho=0.5: 14
Number of categories rho=0.7: 12

The variation between vigilance parameter, number of categories created during learning, and testing performance/generalization is a central theme in ART-based algorithms.

Visualization

Now, to visualize how the two models differ in how they partition the data, we can use principal component analysis (PCA) to compress to two plotting dimensions. PCA is a method to represent a dataset in a different number of dimensions while preserving the relative separation between datapoints. Though most datasets are not able to be effectively transformed down to two dimensions, this technique is useful to get a general sense of how well separated the classes are and how well your algorithm classifies them.

# Train a PCA model to visually separate the features in two dimensions.
M = fit(PCA, features; maxoutdim=2)

# Apply the PCA model to the testing set
X_test_pca = MultivariateStats.transform(M, X_test)
2×45 Matrix{Float64}:
 -1.58527   2.46906   -0.234541  -0.262336  2.54323   2.59716  -1.94925    -0.375238   0.751467  -2.34976    2.867      -1.22043   -1.11624   -2.16538   2.67384   2.38387  -0.920503   2.63285   -0.164513  -0.355533  -3.48877  -1.16885   -0.942362  -0.356787   2.64354  -2.12285   -2.41939    2.88982    2.82089    2.30313    2.71567   -1.25763   -2.3143     0.707081  -0.134995  -0.714008  -1.41407   -1.41407   -0.932411  -2.93201   2.40551   2.20883   -2.61648   -1.94402    3.2252
 -0.539307  0.137887  -0.331922  -0.547893  0.440032  1.10002   0.0407303  -0.291622  -1.00111   -0.0418825  0.0771931   0.408035  -0.084214   0.21528  -0.106692  1.34475  -0.18239   -0.190076  -0.679661  -0.503218   1.17154  -0.164502  -0.541822  -0.0668238  1.18619  -0.210855   0.303504  -0.137346  -0.0821045  0.105523  -0.242681  -0.179137   0.182609  -1.00842   -0.31171    0.150379  -0.574925  -0.574925   0.319198   0.352377  0.195917  0.442696   0.341935   0.187415  -0.50328

We can now plot the PCA'ed test set and label them according to the two FuzzyART's We will do so by creating a function for the subplots first as they will share the same format, and we dare not duplicate code. Then, we will plot those subplots side-by-side.

# Create a function for our subplots
function fuzzyart_scatter(data, labels, rho)
    p = scatter(
        data[1, :],             # PCA dimension 1
        data[2, :],             # PCA dimension 2
        group=labels,           # labels belonging to each point
        markersize=8,           # size of scatter points
        xlims = [-4, 4],        # manually set the x-limits
        title=(@sprintf "FuzzyART \$\\rho\$ = %.1f" rho),  # formatted title
    )
    return p
end

# Create the two scatterplot objects
p1 = fuzzyart_scatter(X_test_pca, y_hat_1, rho_1)
p2 = fuzzyart_scatter(X_test_pca, y_hat_2, rho_2)

# Plot the two scatterplots together
plot(
    p1, p2,                 # scatterplot objects
    layout = (1, 2),        # plot side-by-side
    ##layout = [a, b],        # plot side-by-side
    legend = false,         # no legend
    xtickfontsize = 12,     # x-tick size
    ytickfontsize = 12,     # y-tick size
    xlabel = "\$PCA_1\$",   # x-label
    ylabel = "\$PCA_2\$",   # y-label
    dpi = 300,              # Set the dots-per-inch
)

We can see that the two different vigilance values result in similar resutls on the whole, though they differ in how they classify certain samples that straddle the border between

"assets/options-cover.png"

This page was generated using DemoCards.jl and Literate.jl.