rstudio / keras3

R Interface to Keras
https://keras3.posit.co/
Other
833 stars 282 forks source link

Incorrect array dimensions for poker cards #1024

Open systats opened 4 years ago

systats commented 4 years ago

Hi folks, I get an input error when trying to represent 7 poker cards as one observation. The objective is to predict the winning hand. Each poker card is a 0L matrix with 4(suits)*13(values) elements and only one 1 if the card slot is populated. The representation of one card 9Heart could look like

__123456789TBQKA C 0000000000000 S 0000000000000 H 0000000100000 D 0000000000000

where each observation is supposed to hold 7 of these card matrices.

# repex
cards_encoded <- c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

length(cards_encoded)
4*13*7*2 # 4 suits (H, S, C, D) * 13 values (2-Ace) * 7 cards (hand + board) *  2 player

The input length matches the expected size. The question is how to reshape the data into 2 player observations containing each 7 card matrices (like video frames or timeseries).

# does not work, but looks good 
dim(cards_encoded) <- c(4, 13, 7, 2)

# works, but only has 2 rows instead of 4, 4 columns instead of 13 -> representation is clearly wrong
dim(cards_encoded) <- c(2, 4, 13, 7)

Simple model

inp1 <- keras::layer_input(shape = c(4, 13, 7))

block1 <- inp1 %>%
  keras::layer_conv_2d(filters = 30, kernel_size = c(3, 3), activation = "relu") %>%
  keras::layer_average_pooling_2d(pool_size = c(1, 1)) %>%
  keras::layer_flatten()

out <- block1 %>% 
  keras::layer_dense(activation = "sigmoid", units = 1)

m <- keras::keras_model(inp1, out) %>% 
  keras::compile(optimizer =  "adam", loss = "binary_crossentropy", metric = "accuracy")

m %>% fit(cards_encoded, y = c(0, 1), epochs = 2)

I probably miss here something very fundamental like the right ordering of the card channel. Any help would be highly appreciated.

Thanks in advance...

Simon

turgut090 commented 4 years ago

Hi, did you try this function: example = keras::array_reshape(cards_encoded,dim = c(4, 13, 7, 2))

systats commented 4 years ago

Yes, I guess this equal to dim(cards_encoded) <- c(4, 13, 7, 2). The model error with that encoding is

expected input_9 to have shape (4, 13, 7) but got array with shape (13, 7, 2)

Hard coding the sample size into the model is most likely not the solution. I had a look into object detection with convlstm and the proposed solution is probably

# dim = c(n_samples, n_frames, rows, cols)
array_reshape(cards_encoded, dim = c(2, 7, 4, 13))

It is a bit confusing and the matrix output does not match my expectations, but this maybe something good :D

I have to check whether this works out and learns. Any other ideas are welcomed ;)

turgut090 commented 4 years ago

I put it into tf and it looks like this:

tf$reshape(cards_encoded,c(7L, 8L, 13L))
# 8 rows because 2 players with 4 cards
# 7 matrices/cards 
library(tensorflow)
tensor = tf$reshape(cards_encoded,c(7L, 8L, 13L))
tensor
tf.Tensor(
[[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

 [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]]

 [[0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

 [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

 [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

 [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

 [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]], shape=(7, 8, 13), dtype=float32)

Then, to fit it as input I think we need to expand dims.

fd_tensor = tf$expand_dims(tf$reshape(cards_encoded,c(7L, 8L, 13L)),0L)

inp1 <- keras::layer_input(shape = c(7L, 8L, 13L))

block1 <- inp1 %>%
  keras::layer_conv_2d(filters = 30, kernel_size = c(3, 3), activation = "relu") %>%
  keras::layer_average_pooling_2d(pool_size = c(1, 1)) %>%
  keras::layer_flatten()

out <- block1 %>% 
  keras::layer_dense(activation = "sigmoid", units = 1)

m <- keras::keras_model(inp1, out) %>% 
  keras::compile(optimizer =  "adam", loss = "binary_crossentropy", metric = "accuracy")

m %>% keras::fit(fd_tensor, y = c(1), epochs = 5,batch_size=1)

This one might be more appropriate

library(tensorflow)

tensor = tf$reshape(cards_encoded,c(7L, 2L, 4L, 13L))
tensor

fd_tensor = tf$expand_dims(tf$reshape(cards_encoded,c(7L, 2L, 4L, 13L)),0L)

inp1 <- keras::layer_input(shape = c(7L, 2L, 4L, 13L))

block1 <- inp1 %>%
  keras::layer_flatten()

out <- block1 %>% 
  keras::layer_dense(activation = "sigmoid", units = 1)

m <- keras::keras_model(inp1, out) %>% 
  keras::compile(optimizer =  "adam", loss = "binary_crossentropy", metric = "accuracy")

m %>% keras::fit(fd_tensor, y = c(0), epochs = 5,batch_size=1)
systats commented 4 years ago

This looks very promising. Especially the output tensor is quit intuitive to understand

cards_spatial <- tf$reshape(cards_spatial, c(nrow(states), 7L, 4L, 13L))
dim(cards_spatial)
# 107849      7      4     13

is even better because one game state representing seven cards per player is independent of n_player (which is only a relational feature of the game state). The agent sees only his two cards and the board cards at a time.

Thank you very very much! It seems to work ;)