Two-class classification model with multi-type input data
With the help from a colleague, we came up with this:

set.seed(666)

#Dummy data
dat &lt;- data.frame(samples=c(rep(paste0("A_",1:9800)),rep(paste0("B_",9801:10000))))
dat$label &lt;- c(rep("A",9800),rep("B",200))
dat$continuous1 &lt;- c(rnorm(9800, 100, 45),rnorm(200, 0, 25))
dat$continuous1[dat$continuous1&lt;0] &lt;- 0
dat$categorical1 &lt;- c(rep(1:100,98),rep(1:100,2))
dat$categorical2 &lt;- c(rep(1:98,100),rep(99:100,100))

pool_A &lt;- factor(1:15,levels=1:20)
pool_B &lt;- factor(16:20,levels=1:20)
dat_categorical3 &lt;- vector("list",10000)
names(dat_categorical3) &lt;- c(as.character(dat[dat$label=="A",]$samples),as.character(dat[dat$label=="B",]$samples))
for(i in 1:9800){
  dat_categorical3[[i]] &lt;- (as.numeric(table(sample(pool_A, 20, replace=T))) &gt; 0) + 0L
}
for(i in 9801:10000){
  dat_categorical3[[i]] &lt;- (as.numeric(table(sample(pool_B, 20, replace=T))) &gt; 0) + 0L
}
dat_categorical3_tensor &lt;- do.call(rbind,dat_categorical3)


## Split data for training-validating-testing

# As we have much fewer Bs than As, each partition has to have a more or less equal number of Bs(?)
training_Bs &lt;- sample(x=as.character(dat[dat$label=="B",]$samples), size=67, replace=F)
validating_Bs &lt;- sample(x=setdiff(as.character(dat[dat$label=="B",]$samples),training_Bs), size=67, replace=F)
testing_Bs &lt;- setdiff(as.character(dat[dat$label=="B",]$samples),c(training_Bs,validating_Bs))

# We also parition the data containing As equally, though this could probably be done in e.g., 80-10-10
training_As &lt;- sample(x=as.character(dat[dat$label=="A",]$samples), size=3267, replace=F)
validating_As &lt;- sample(x=setdiff(as.character(dat[dat$label=="A",]$samples),training_As), size=3267, replace=F)
testing_As &lt;- setdiff(as.character(dat[dat$label=="A",]$samples),c(training_As,validating_As))

# Put together
training_dat &lt;- dat[dat$samples %in% c(training_As,training_Bs),]
training_categorical3_tensor &lt;- dat_categorical3_tensor[rownames(dat_categorical3_tensor) %in% c(training_As,training_Bs),]
training_labels &lt;- ifelse(training_dat$label=="A",0,1)
training_dat2 &lt;- as.matrix(training_dat[,c(3:5)]) #use this

validating_dat &lt;- dat[dat$samples %in% c(validating_As,validating_Bs),]
validating_categorical3_tensor &lt;- dat_categorical3_tensor[rownames(dat_categorical3_tensor) %in% c(validating_As,validating_Bs),]
validating_labels &lt;- ifelse(validating_dat$label=="A",0,1) 
validating_dat2 &lt;- as.matrix(validating_dat[,c(3:5)]) #use this

testing_dat &lt;- dat[dat$samples %in% c(testing_As,testing_Bs),]
testing_categorical3_tensor &lt;- dat_categorical3_tensor[rownames(dat_categorical3_tensor) %in% c(testing_As,testing_Bs),]
testing_labels &lt;- ifelse(testing_dat$label=="A",0,1)
testing_dat2 &lt;- as.matrix(testing_dat[,c(3:5)]) #use this


## Keras model

# Input layers
all_dat_input &lt;- layer_input(shape = 3, dtype = 'float32', name = 'all_dat_input')
categorical3_indices_input &lt;- layer_input(shape = 20, dtype = 'float32', name = 'categorical3_input')
input_tensor &lt;- c(all_dat_input, categorical3_indices_input)

# Output layers
all_dat_out &lt;- all_dat_input %&gt;%
  layer_dense(units = 128, activation = 'relu') %&gt;%
  layer_dropout(rate = 0.5) %&gt;%
  layer_dense(units = 128, activation = 'relu') %&gt;%
  layer_dropout(rate = 0.5)

categorical3_indices_out &lt;- categorical3_indices_input %&gt;%
  layer_dense(units = 128, activation = 'relu') %&gt;%
  layer_dropout(rate = 0.5) %&gt;%
  layer_dense(units = 128, activation = 'relu') %&gt;%
  layer_dropout(rate = 0.5)

output_tensor &lt;- layer_concatenate(c(all_dat_out, categorical3_indices_out)) %&gt;%
  layer_dense(units = 128, activation = 'relu') %&gt;%
  layer_dropout(rate = 0.5) %&gt;%
  layer_dense(units = 128, activation = 'relu') %&gt;%
  layer_dropout(rate = 0.5) %&gt;%
  layer_dense(units=1, activation="sigmoid")

model &lt;- keras_model(inputs=input_tensor, outputs=output_tensor)

# Compile
model %&gt;% compile(
  optimizer = "rmsprop",
  loss = "binary_crossentropy",
  metrics = "accuracy"
)

# Fit
history &lt;- model %&gt;% fit(
  x=list(training_dat2,training_categorical3_tensor),
  y=training_labels,
  batch_size=256,
  epochs=20,
  validation_data=list(list(validating_dat2,validating_categorical3_tensor),validating_labels)
)




# Compare with put aside testing data
results &lt;- model %&gt;% evaluate(list(testing_dat2,testing_categorical3_tensor),testing_labels)

results
$loss
[1] 0.0000001584833
$acc
[1] 1

# Using the trained network to generate predictions on new data
predictions &lt;- model %&gt;% predict(list(testing_dat2,testing_categorical3_tensor))

head(predictions[3267:3332])
[1] 0.9999921 0.9999992 0.9999999 0.9999996 0.9999999 0.9999542    
# the network correctly identifies Bs as Bs with a confidence between &gt;99%

(Remember that this is dummy data!)


I'm currently looking into feature importance to see which data inputs contributed more than others to the prediction. lime seems to be made for this!