Search code examples
pythontensorflowmachine-learningkerasrecommendation-engine

What shape does my Input layer need to be? I keep getting ValueError: Layer "model" expects 1 input(s), but it received 2 input tensors


This is taking the code from this project and converting to the Keras functional API. I un-subclassed the model from here and re-define it starting at inputs.

The snag I'm running into is I get the following error when I try to fit the model.

ValueError: Layer "model" expects 1 input(s), but it received 2 input tensors. Inputs received: 
[<tf.Tensor 'IteratorGetNext:0' shape=(None,) dtype=int64>, <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>]

So I must have the shape of the initial tensor wrong- but I'm unsure how to proceed as I've tried a few different ways and nothing seems to make sense.

import math
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from tensorflow import keras
from tensorflow.keras import layers
# Import CF Model Architecture
from CFModel import CFModel

RNG_SEED = 42

# Reading ratings file
ratings = pd.read_csv('data/ratings.csv', sep='\t', encoding='latin-1', 
                      usecols=['user_id', 'movie_id', 'user_emb_id', 'movie_emb_id', 'rating'])
max_userid = ratings['user_id'].drop_duplicates().max()
max_movieid = ratings['movie_id'].drop_duplicates().max()

# Reading ratings file
users = pd.read_csv('data/users.csv', sep='\t', encoding='latin-1', 
                    usecols=['user_id', 'gender', 'zipcode', 'age_desc', 'occ_desc'])

# Reading ratings file
movies = pd.read_csv('data/movies.csv', sep='\t', encoding='latin-1', 
                     usecols=['movie_id', 'title', 'genres'])

# Create training set
shuffled_ratings = ratings.sample(frac=1., random_state=RNG_SEED)

# Shuffling users
Users = shuffled_ratings['user_emb_id'].values
print( 'Users:', Users, ', shape =', Users.shape)
# Users: [5411 5439  367 ...  853 4032  785] , shape = (1000209,)

# Shuffling movies
Movies = shuffled_ratings['movie_emb_id'].values
print( 'Movies:', Movies, ', shape =', Movies.shape)
# Movies: [2682  903 3716 ... 3101 3478 1390] , shape = (1000209,)

# Shuffling ratings
Ratings = shuffled_ratings['rating'].values
print( 'Ratings:', Ratings, ', shape =', Ratings.shape)
# Ratings: [2 5 4 ... 3 5 4] , shape = (1000209,)

K_FACTORS = 100 # The number of dimensional embeddings for movies and users
TEST_USER = 2000 # A random test user (user_id = 2000)
# Define model

inputs = layers.Input(shape=(1000209, 100)) # What should this input be? 

embeddingUsers = layers.Embedding(max_userid, K_FACTORS, input_length=1)
usersEmbedded = embeddingUsers(inputs)

embeddingMovies = layers.Embedding(max_movieid, K_FACTORS, input_length=1)
moviesEmbedded = embeddingMovies(inputs)

inputs = layers.Dot(axes=1)([usersEmbedded, moviesEmbedded])

outputs = layers.Dense(5, activation='relu')(inputs)

model = keras.Model(inputs=inputs, outputs=outputs)
keras.utils.plot_model(model, "my_first_model_with_shape_info.png", show_shapes=True)

print(model.summary())
#  Layer (type)                Output Shape              Param #
# =================================================================
#  input_2 (InputLayer)        [(None, 100, 100, 100, 1  0
#                              00)]

#  dense (Dense)               (None, 100, 100, 100, 5)  505

# =================================================================
# Total params: 505
# Trainable params: 505
# Non-trainable params: 0
# _________________________________________________________________

model.compile(optimizer="rmsprop", loss="mse")

history = model.fit(
    [Users, Movies],
    Ratings,
    epochs=10,
    batch_size=32,
    validation_split=0.2,
) 

# ValueError: Layer "model" expects 1 input(s), but it received 2 input tensors. Inputs received: 
# [<tf.Tensor 'IteratorGetNext:0' shape=(None,) dtype=int64>, <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>]

Solution

  • I think you are mixing up a few things. The CFModel, which you referenced and are trying to convert into a Model with the functional API, consists of two Sequential sub-models. If you want to pass two inputs to your model, it should have two Input layers:

    import tensorflow as tf
    
    max_userid = 5000
    max_movieid = 5000
    K_FACTORS = 100
    user_input = tf.keras.layers.Input(shape=(1,))
    movie_input = tf.keras.layers.Input(shape=(1,))
    
    embedding_users = tf.keras.layers.Embedding(max_userid, K_FACTORS, input_length=1)
    users_embedded = embedding_users(user_input)
    
    embedding_movies = tf.keras.layers.Embedding(max_movieid, K_FACTORS, input_length=1)
    movies_embedded = embedding_movies(movie_input)
    
    inputs = tf.keras.layers.Dot(axes=1)([users_embedded, movies_embedded])
    outputs = tf.keras.layers.Dense(5, activation='relu')(inputs)
    model = tf.keras.Model(inputs=[user_input, movie_input], outputs=outputs)
    
    tf.keras.utils.plot_model(model, "my_first_model_with_shape_info.png", show_shapes=True)
    
    print(model.summary())
    
    model.compile(optimizer="rmsprop", loss="mse")
    
    history = model.fit(
        [Users, Movies],
        Ratings,
        epochs=10,
        batch_size=32,
        validation_split=0.2,
    ) 
    
    Model: "model_1"
    __________________________________________________________________________________________________
     Layer (type)                   Output Shape         Param #     Connected to                     
    ==================================================================================================
     input_4 (InputLayer)           [(None, 1)]          0           []                               
                                                                                                      
     input_5 (InputLayer)           [(None, 1)]          0           []                               
                                                                                                      
     embedding_2 (Embedding)        (None, 1, 100)       500000      ['input_4[0][0]']                
                                                                                                      
     embedding_3 (Embedding)        (None, 1, 100)       500000      ['input_5[0][0]']                
                                                                                                      
     dot_1 (Dot)                    (None, 100, 100)     0           ['embedding_2[0][0]',            
                                                                      'embedding_3[0][0]']            
                                                                                                      
     dense_1 (Dense)                (None, 100, 5)       505         ['dot_1[0][0]']                  
                                                                                                      
    ==================================================================================================
    Total params: 1,000,505
    Trainable params: 1,000,505
    Non-trainable params: 0
    __________________________________________________________________________________________________
    None
    

    enter image description here

    Since both Users and Movies have the shape (1000209,), your input shape for both Input layers is shape=(1,). This means that each sample consists of an ID that is mapped to a 100-dimensional vector using the Embedding layers. Oh, and just in case it's not intentional, the CFModel does not use a Dense layer after calculating the dot product.