VNNikolaidis / nnlib2Rcpp

An R package for Neural Nets created using nnlib2
Other
13 stars 4 forks source link

Reusing saved trained NN #15

Closed drag05 closed 7 months ago

drag05 commented 7 months ago

I am having a bit of a problem with reusing a saved trained NN. Let's take the sLVQ-class example from the Manual: the lvq is trained on iris_s. Now:

> class(lvq)
[1] "LVQs"
attr(,"package")
[1] "nnlib2Rcpp"
> typeof(lvq)
[1] "S4"

Saving the NN:

save(lvq, file = 'LVQ.txt')

Removing the NN from global env and checking the existence of other objects:

rm(lvq)

> ls() ## no lvq presence in global env
[1] "c_max"                  "c_min"                  "c_rng"                  "iris_desired_class_ids"
[5] "iris_s"                 "lvq_recalled_class_ids"

Loading the saved trained NN:

load(file = 'LVQ.txt', verbose = TRUE)
Loading objects:
  lvq

Checking:

> ls() ## lvq is now present in global env
[1] "c_max"                  "c_min"                  "c_rng"                  "iris_desired_class_ids"
[5] "iris_s"                 "lvq"                    "lvq_recalled_class_ids"

> class(lvq)
[1] "LVQs"
attr(,"package")
[1] "nnlib2Rcpp"
> typeof(lvq)
[1] "S4"

recall and show now throw this error:

lvq$recall(iris_s)
Error in .External(list(name = "CppMethod__invoke_notvoid", address = <pointer: (nil)>,  : 
  NULL value passed as symbol address

show(lvq)
Error in .External(list(name = "CppMethod__invoke_void", address = <pointer: (nil)>,  : 
  NULL value passed as symbol address

I think I am missing something, please advise, thank you!

VNNikolaidis commented 7 months ago

Now I may be wrong, but my belief is that C++ objects, functions etc. that have been connected to R via RCpp (the approach taken here) cannot directly be saved and retrieved via R’s load and save functions (the ones provided by the base package). Look for example here where they attempt to save a function, but the issues should be similar. See also: here.

Fortunately, the suggestion towards what you want to do is using the load and save methods provided by the nnlib2Rcpp modules themselves (see for example the save and load methods listed in help(LVQs).

For completeness, below is the documentation example you mention (and tried):

library(nnlib2Rcpp)

# LVQ expects data in 0 to 1 range, so scale some numeric data...
iris_s<-as.matrix(iris[1:4])
c_min<-apply(iris_s,2,FUN = "min")
c_max<-apply(iris_s,2,FUN = "max")
c_rng<-c_max-c_min
iris_s<-sweep(iris_s,2,FUN="-",c_min)
iris_s<-sweep(iris_s,2,FUN="/",c_rng)

# create a vector of desired class ids (starting from 0):
iris_desired_class_ids<-as.integer(iris$Species)-1;

# now create the NN:
lvq<-new("LVQs")

# and train it:
lvq$encode(iris_s,iris_desired_class_ids,100)

# recall the same data (a simple check of how well the LVQ was trained):
lvq_recalled_class_ids<-lvq$recall(iris_s);
plot(iris_s, pch=lvq_recalled_class_ids, main="LVQ recalled clusters (module)")

In the example, the lvq object is trained on iris_s. It's state can be stored on a file using the save method:

lvq$save('lvq.txt')

You can now remove the original lvq, close the R session etc.

To restore it, create a new LVQ NN and load the state from the file (note: I am using same variable name lvq but obviously any other name is fine):

lvq<-new("LVQs")
lvq<-load('lvq.txt')

You should now be able to use it as normal (recall data, continue training etc.). I hope this fits your purpose. If so, please close the issue.

drag05 commented 7 months ago

@VNNikolaidis Of course! It seems a case of tunnel-vision on my part jumping to base::save without thinking. I am closing this now. Thank you!

VNNikolaidis commented 7 months ago

You were right to jump to base::save as this IS the proper way to save R objects. Unfortunately, when the object "lives" outside R, then this issue arises (it is an issue for many useful R extension packages as discussed here ).

drag05 commented 7 months ago

@VNNikolaidis Thank you for the link! I remember reading this Vignette a while back when I was implementing futures in a Shiny app for querying a database. I wish future had a list of explained options inside the Manual for quick reference, similar to pander.