Skip to content

Commit

Permalink
Merge pull request #139 from AP6YC/release/v0.8.5
Browse files Browse the repository at this point in the history
Release/v0.8.5
  • Loading branch information
AP6YC authored Sep 24, 2024
2 parents 58e4ebf + 1db5c5a commit 36c5c67
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 42 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/draft-pdf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
# This should be the path to the paper within your repo.
paper-path: paper/paper.md
- name: Upload
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
with:
name: paper
# This is the output path where Pandoc will write the compiled
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name = "AdaptiveResonance"
uuid = "3d72adc0-63d3-4141-bf9b-84450dd0395b"
authors = ["Sasha Petrenko"]
description = "A Julia package for Adaptive Resonance Theory (ART) algorithms."
version = "0.8.4"
version = "0.8.5"

[deps]
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
Expand Down
2 changes: 2 additions & 0 deletions docs/src/man/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ Though most parameters differ between each ART and ARTMAP module, they all share

- `display::Bool`: a flag to display or suppress progress bars and logging messages during training and testing.
- `max_epochs::Int`: the maximum number of epochs to train over the data, regardless if other stopping conditions have not been met yet.
- `sort::Bool`: if a sort procedure on the activations is done before the match rule.
This is false by default for all modules, using instead an `argmax` and node deactivation strategy for evaluating the vigilance criterion, which is faster in *most* cases.

Otherwise, most ART and ARTMAP modules share the following nomenclature for algorithmic parameters:

Expand Down
54 changes: 48 additions & 6 deletions src/ART/distributed/modules/DDVFA.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ $(_OPTS_DOCSTRING)
Selected weight update function.
"""
update::Symbol = :basic_update

"""
Flag to sort the F2 nodes by activation before the match phase
When true, the F2 nodes are sorted by activation before match.
When false, an iterative argmax and inhibition procedure is used to find the best-matching unit.
"""
sort::Bool = false
end

# -----------------------------------------------------------------------------
Expand Down Expand Up @@ -228,6 +236,7 @@ function DDVFA(opts::opts_DDVFA)
activation=opts.activation,
match=opts.match,
update=opts.update,
sort=opts.sort,
)

# Construct the DDVFA module
Expand Down Expand Up @@ -283,6 +292,7 @@ function train!(art::DDVFA, x::RealVector ; y::Integer=0, preprocessed::Bool=fal

# Default to mismatch
mismatch_flag = true
y_hat = -1

# Compute the activation for all categories
accommodate_vector!(art.T, art.n_categories)
Expand All @@ -292,11 +302,22 @@ function train!(art::DDVFA, x::RealVector ; y::Integer=0, preprocessed::Bool=fal
end

# Compute the match for each category in the order of greatest activation
index = sortperm(art.T, rev=true)
if art.opts.sort
index = sortperm(art.T, rev=true)
top_bmu = index[1]
else
top_bmu = argmax(art.T)
end

accommodate_vector!(art.M, art.n_categories)
for jx = 1:art.n_categories
# Best matching unit
bmu = index[jx]
if art.opts.sort
bmu = index[jx]
else
bmu = argmax(art.T)
end

# Compute the match with the similarity linkage method
art.M[bmu] = similarity(art.opts.similarity, art.F2[bmu], sample, false)
# If we got a match, then learn (update the category)
Expand All @@ -305,22 +326,28 @@ function train!(art::DDVFA, x::RealVector ; y::Integer=0, preprocessed::Bool=fal
if supervised && (art.labels[bmu] != y)
break
end

# Update the weights with the sample
train!(art.F2[bmu], sample, preprocessed=true)
# Save the output label for the sample
y_hat = art.labels[bmu]
# No mismatch
mismatch_flag = false
break
elseif !art.opts.sort
# Remove the top activation
art.T[bmu] = 0.0
end
end

# If we triggered a mismatch
if mismatch_flag
# Keep the bmu as the top activation despite creating a new category
bmu = index[1]
bmu = top_bmu

# Get the correct label
y_hat = supervised ? y : art.n_categories + 1

# Create a new category
create_category!(art, sample, y_hat)
end
Expand All @@ -346,16 +373,27 @@ function classify(art::DDVFA, x::RealVector ; preprocessed::Bool=false, get_bmu:
end

# Sort by highest activation
index = sortperm(art.T, rev=true)
if art.opts.sort
index = sortperm(art.T, rev=true)
top_bmu = index[1]
else
top_bmu = argmax(art.T)
end

# Default to mismatch
mismatch_flag = true
y_hat = -1

# Iterate over the list of activations
accommodate_vector!(art.M, art.n_categories)
for jx = 1:art.n_categories
# Get the best-matching unit
bmu = index[jx]
if art.opts.sort
bmu = index[jx]
else
bmu = argmax(art.T)
end

# Get the match value of this activation
art.M[bmu] = similarity(art.opts.similarity, art.F2[bmu], sample, false)
# If the match satisfies the threshold criterion, then report that label
Expand All @@ -366,14 +404,18 @@ function classify(art::DDVFA, x::RealVector ; preprocessed::Bool=false, get_bmu:
y_hat = art.labels[bmu]
mismatch_flag = false
break
elseif !art.opts.sort
# Remove the top activation
art.T[bmu] = 0.0
end
end

# If we did not find a resonant category
if mismatch_flag
# Update the stored match and activation values of the best matching unit
bmu = index[1]
bmu = top_bmu
log_art_stats!(art, bmu, true)

# Report either the best matching unit or the mismatch label -1
y_hat = get_bmu ? art.labels[bmu] : -1
end
Expand Down
8 changes: 8 additions & 0 deletions src/ART/distributed/modules/MergeART.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ $(_OPTS_DOCSTRING)
Selected weight update function.
"""
update::Symbol = :basic_update

"""
Flag to sort the F2 nodes by activation before the match phase
When true, the F2 nodes are sorted by activation before match.
When false, an iterative argmax and inhibition procedure is used to find the best-matching unit.
"""
sort::Bool = false
end

"""
Expand Down
65 changes: 54 additions & 11 deletions src/ART/single/modules/DVFA.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ $(_OPTS_DOCSTRING)
Selected weight update function.
"""
update::Symbol = :basic_update

"""
Flag to sort the F2 nodes by activation before the match phase
When true, the F2 nodes are sorted by activation before match.
When false, an iterative argmax and inhibition procedure is used to find the best-matching unit.
"""
sort::Bool = false
end

"""
Expand Down Expand Up @@ -283,24 +291,34 @@ function train!(art::DVFA, x::RealVector ; y::Integer=0, preprocessed::Bool=fals
# Compute the activation and match for all categories
activation_match!(art, sample)
# Sort activation function values in descending order
index = sortperm(art.T, rev=true)
if art.opts.sort
index = sortperm(art.T, rev=true)
top_bmu = index[1]
else
top_bmu = argmax(art.T)
end

# Default to mismatch
mismatch_flag = true
# Loop over all categories
for j = 1:art.n_categories
for jx = 1:art.n_categories
# Best matching unit
bmu = index[j]
# If supervised and the label differs, trigger mismatch
if supervised && (art.labels[bmu] != y)
break
if art.opts.sort
bmu = index[jx]
else
bmu = argmax(art.T)
end

# Vigilance test upper bound
if art.M[bmu] >= art.threshold_ub
# If supervised and the label differs, trigger mismatch
if supervised && (art.labels[bmu] != y)
break
end

# Learn the sample
learn!(art, sample, bmu)
# Update sample label for output
# y_hat = supervised ? y : art.labels[bmu]
y_hat = art.labels[bmu]
# No mismatch
mismatch_flag = false
Expand All @@ -314,15 +332,20 @@ function train!(art::DVFA, x::RealVector ; y::Integer=0, preprocessed::Bool=fals
# No mismatch
mismatch_flag = false
break
elseif !art.opts.sort
# Remove the top activation
art.T[bmu] = 0.0
end
end

# If there was no resonant category, make a new one
if mismatch_flag
# Keep the bmu as the top activation despite creating a new category
bmu = index[1]
bmu = top_bmu

# Create a new category-to-cluster label
y_hat = supervised ? y : art.n_clusters + 1

# Create a new category
create_category!(art, sample, y_hat)
end
Expand All @@ -341,23 +364,43 @@ function classify(art::DVFA, x::RealVector ; preprocessed::Bool=false, get_bmu::
# Compute activation and match functions
activation_match!(art, sample)
# Sort activation function values in descending order
index = sortperm(art.T, rev=true)
if art.opts.sort
index = sortperm(art.T, rev=true)
top_bmu = index[1]
else
top_bmu = argmax(art.T)
end

# Default to mismatch
mismatch_flag = true
y_hat = -1

# Iterate over the list of activations
for jx in 1:art.n_categories
bmu = index[jx]
# Get the best-matching unit
if art.opts.sort
bmu = index[jx]
else
bmu = argmax(art.T)
end

# Vigilance check - pass
if art.M[bmu] >= art.threshold_ub
# Current winner
y_hat = art.labels[bmu]
mismatch_flag = false
break
elseif !art.opts.sort
# Remove the top activation
art.T[bmu] = 0.0
end
end

# If we did not find a resonant category
if mismatch_flag
# Create new weight vector
bmu = index[1]
bmu = top_bmu

# Report either the best matching unit or the mismatch label -1
y_hat = get_bmu ? art.labels[bmu] : -1
end
Expand Down
Loading

2 comments on commit 36c5c67

@AP6YC
Copy link
Owner Author

@AP6YC AP6YC commented on 36c5c67 Sep 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register

Release notes:

This patch adds an option for how the WTA competition is conducted for all existing modules; the category competitions may now go an argmax and suppress procedure or be presorted.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/115860

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.8.5 -m "<description of version>" 36c5c679027919c6cc78b81555bef68285264c45
git push origin v0.8.5

Please sign in to comment.