Search code examples
rkerasneural-network

Keras model in R


I am trying to run a neural network model in R, using Keras library. The idea is very simple, and a neural network model for this problem is overkill, but I need this for testing. I know I could reach my results in other ways, but the problem I am facing is not that simple. The coordinates are not so nicely distributed and there are also other categorical features. I need this test such that I can generalize it to this other problem I have.

In this case, I generate some samples. I generate 100 samples, with a maximum number of points of 1k. In the first 100 the x_coordinate is generated using: rnorm(a_num, 10, 4) and the y_coordinate using: rnorm(a_num, 20, 8) Each sample has a variable number of points. Using zero padding I add zeros until we reach 1k points total.

num_points=1000
all_data=NULL
for(i in 1:100){
  
  a_num=sample(num_points, 1)
  adf=data.frame(
    ID=i,
    x_coord=rnorm(a_num, 10, 4),
    y_coord=rnorm(a_num, 20, 8)
  )
  ### zero padding until we reach 1k points
  adf=rbind(adf, data.frame(ID=i, x_coord=rep(0, num_points-a_num), y_coord=rep(0, num_points-a_num)))
  adf=adf[sample(1:nrow(adf)), ]
  
  all_data=rbind(all_data, adf)
  
}

I then generate another set of samples. These with coordinates rnorm(a_num, -4, 4), and rnorm(a_num, -8, 8). Also in this case I use zero padding until they reach 1k points.

for(i in 101:200){
  
  a_num=sample(num_points, 1)
  adf=data.frame(
    ID=i,
    x_coord=rnorm(a_num, -4, 4),
    y_coord=rnorm(a_num, -8, 8)
  )
  ### zero padding until we reach 1k points
  adf=rbind(adf, data.frame(ID=i, x_coord=rep(0, num_points-a_num), y_coord=rep(0, num_points-a_num)))
  adf=adf[sample(1:nrow(adf)), ]
  
  all_data=rbind(all_data, adf)
  
}
  

The first hundred samples are in the category "1" and the second hundred in the category "0".

  the_category=data.frame(ID=1:200, cat=c(rep(1, 100), rep(0, 100)))
  

I convert each sample from columns to rows. Now, in each row, we have all the x coordinates and all the y coordinates for each sample.

  df=as.data.table(all_data)[, dcast(.SD, ID ~ rowid(ID), value.var = names(all_data)[-1])]
  
  df <- as.data.frame(df)

After I build my model. I generate the list "indexes" to split the dataset in training set and test set. I also generate the labels for the training and the test set.

# split the dataset into train and test sets
indexes <- sample(1:nrow(df), size = 0.7*nrow(df))
train_df_x <- df[indexes, 2:ncol(df)]
label_train= the_category$cat[match( df[indexes, "ID"], the_category$ID)]

test_df_x <- df[-indexes, 2:ncol(df)]
label_test=the_category$cat[match(df[-indexes, "ID"], the_category$ID)]


library(keras)

# Define the model architecture
{
  model <- keras_model_sequential()
  model %>% layer_dense(units = 128, activation = "relu", input_shape = ncol(test_df_x))
  model %>% layer_dropout(rate = 0.3)
  model %>% layer_dense(units = 64, activation = "relu")
  model %>% layer_dropout(rate = 0.3)
  model %>% layer_dense(units = 32, activation = "relu")
  model %>% layer_dropout(rate = 0.3)
  model %>% layer_dense(units = 1, activation = "sigmoid")
}
  # Compile the model
  model %>% compile(
    optimizer = "adam",
    loss = "binary_crossentropy",
    metrics = c("accuracy")  )
  
  # Fit the model on the training data
  model %>% fit(
    train_df_x,
    label_train,
    epochs = 100,
    batch_size = 1,
    validation_split = 0.1 )

Unfortunately, I never reach this final part since I get this error:

Error in py_call_impl(callable, dots$args, dots$keywords) : 
ValueError: in user code:
<... omitted ...>4', 'y_coord_945', 'y_coord_946', 'y_coord_947', 'y_coord_948', 'y_coord_949', 'y_coord_950', 'y_coord_951', 'y_coord_952', 'y_coord_953', 'y_coord_954', 'y_coord_955', 'y_coord_956', 'y_coord_957', 'y_coord_958', 'y_coord_959', 'y_coord_960', 'y_coord_961', 'y_coord_962', 'y_coord_963', 'y_coord_964', 'y_coord_965', 'y_coord_966', 'y_coord_967', 'y_coord_968', 'y_coord_969', 'y_coord_970', 'y_coord_971', 'y_coord_972', 'y_coord_973', 'y_coord_974', 'y_coord_975', 'y_coord_976', 'y_coord_977', 'y_coord_978', 'y_coord_979', 'y_coord_980', 'y_coord_981', 'y_coord_982', 'y_coord_983', 'y_coord_984', 'y_coord_985', 'y_coord_986', 'y_coord_987', 'y_coord_988', 'y_coord_989', 'y_coord_990', 'y_coord_991', 'y_coord_992', 'y_coord_993', 'y_coord_994', 'y_coord_995', 'y_coord_996', 'y_coord_997', 'y_coord_998', 'y_coord_999', 'y_coord_1000']. Expected the following keys: ['dense_input']

See `reticulate::py_last_error()` for details

And calling py_last_error() gives:

> reticulate::py_last_error()
Traceback (most recent call last):
  File "C:\PROGRA~3\ANACON~1\lib\site-packages\keras\utils\traceback_utils.py", line 70, in error_handler
    raise e.with_traceback(filtered_tb) from None
  File "C:\Users\fa9028\AppData\Local\Temp\__autograph_generated_filezof6pwaa.py", line 15, in tf__train_function
    retval_ = ag__.converted_call(ag__.ld(step_function), (ag__.ld(self), ag__.ld(iterator)), None, fscope)
ValueError: in user code:

File "C:\PROGRA~3\ANACON~1\lib\site-packages\keras\engine\training.py", line 1160, in train_function  *
    return step_function(self, iterator)
File "C:\PROGRA~3\ANACON~1\lib\site-packages\keras\engine\training.py", line 1146, in step_function  **
    outputs = model.distribute_strategy.run(run_step, args=(data,))
File "C:\PROGRA~3\ANACON~1\lib\site-packages\keras\engine\training.py", line 1135, in run_step  **
    outputs = model.train_step(data)
File "C:\PROGRA~3\ANACON~1\lib\site-packages\keras\engine\training.py", line 993, in train_step
    y_pred = self(x, training=True)
File "C:\PROGRA~3\ANACON~1\lib\site-packages\keras\utils\traceback_utils.py", line 70, in error_handler
    raise e.with_traceback(filtered_tb) from None
File "C:\PROGRA~3\ANACON~1\lib\site-packages\keras\engine\input_spec.py", line 197, in assert_input_compatibility
    raise ValueError(

ValueError: Missing data for input "dense_input". You passed a data dictionary with keys ['x_coord_1', ...A_LOT_OF_COLUMN_NAMES..., 'y_coord_1000']. Expected the following keys: ['dense_input']

Following some posts, here in STACK, I tried to change "input_shape" with "input_shape = NULL", or "input_shape = c(1, ncol(test_df_x)" but it didn't help. What am I doing wrong?

This would be the testing part:

# Evaluate the model on the test data
results <- model %>% evaluate(test_df_x, label_test)

# Make predictions on new data
predictions <- model %>% predict(new_data)

Solution

  • It turns out that the function

      model %>% fit(
        train_df_x,
        label_train,
        epochs = 100,
        batch_size = 1,
        validation_split = 0.1
      )
    

    Is not compatible with data frames. The train set (and the test set when doing predictions) must be converted to a matrix.

    The fix is simple, one needs to do the following:

    train_df_x <- as.matrix(train_df_x)
    test_df_x <- as.matrix(test_df_x)
    

    For this simple example, after 100 epoch we have:

    Epoch 100/100
    126/126 [==============================] - 1s 6ms/step - loss: 1.6427e-34 - accuracy: 1.0000 - val_loss: 0.0000e+00 - val_accuracy: 1.0000