Practice Lab - C3_W2_RecSysNN_Assignment

To Whom It May Concern - In the practice lab on content based filtering, I successfully produced predictions for a single new customer and a single existing customer. Is there a way to produce predictions for groups of customers or all customers (e.g. thru vectorization)? Or do I have to loop thru each customer to get ratings predictions for all customers?

If so, that seems like a computational challenge for really large datasets.

Dan

uid = 2 
# form a set of user vectors. This is the same vector, transformed and repeated.
user_vecs, y_vecs = get_user_vecs(uid, user_train_unscaled, item_vecs, user_to_genre)

# scale our user and item vectors
suser_vecs = scalerUser.transform(user_vecs)
sitem_vecs = scalerItem.transform(item_vecs)

# make a prediction
y_p = model.predict([suser_vecs[:, u_s:], sitem_vecs[:, i_s:]])

# unscale y prediction 
y_pu = scalerTarget.inverse_transform(y_p)

HI @danielhopkins80

here you want to do for loop but if you implement the function get_user_vecs by your self you can do it without any for loop

Thanks,
Adbelrahman

Thank you. I took a quick look at the function “get_user_vecs.” This also looks like a loop.

Is there a way to vectorize the prediction? Essentially, we produce a prediction for all movie/user combinations through some matrix multiplication.

Dan

Essentially … Can the scaled user vector user_vecs[:, u_s:] contain multiple users (vs. single user) with the output y_p being a matrix of customer ratings for each movie? Currently, y_p is only a 1-dimensional vector for a single customer with ratings for all movies. I then have to loop thru this process to create the full matrix and I would love to do in the model.predict section of the code.

Seems like its possible to do this.

def predict_uservec(user_vecs, item_vecs, model, u_s, i_s, scaler):
    """ given a scaled user vector, does the prediction on all movies in scaled print_item_vecs returns
        an array predictions sorted by predicted rating,
        arrays of user and item, sorted by predicted rating sorting index
    """
    y_p = model.predict([user_vecs[:, u_s:], item_vecs[:, i_s:]])
    y_pu = scaler.inverse_transform(y_p)

    if np.any(y_pu < 0):
        print("Error, expected all positive predictions")
    sorted_index = np.argsort(-y_pu, axis=0).reshape(-1).tolist()  #negate to get largest rating first
    sorted_ypu   = y_pu[sorted_index]
    sorted_items = item_vecs[sorted_index]
    sorted_user  = user_vecs[sorted_index]
    return(sorted_index, sorted_ypu, sorted_items, sorted_user)```

Hi @danielhopkins80

this photo was in function get_user_vecs using for loop
image

I thinks when we want to implement it without for loop and with vectorization we can do it
user_vec =user_train[user_train[:, 0] == user_id]
if(user_train[:, 0] == user_id):
user_vec_found = True

and so on so I thinks many of these for loop we can do it with vectorization so it will be faster …every one have his own way to implement it

I didn’'t understand all what you want to do in the last question but in function model.predict you can send multiple users to predict it …and user_vecs here is a user vector we can contain multiple users in a big matrix but it will be quite hard to do it and to show to learner that it is a matrix contains multiple vectors but you can do it in your own code if you want to implement every function of these by your own …it is a great practice

finally every one has his own way to implement these function
Thanks,
Adbelrahman

Hi Dan @danielhopkins80 ,

I can give you some hints. This is how the assignment formulates the problem:

And I suppose the following will satisfy your goal:

which outputs a matrix where each row represents an user’s rating on all movies.

This can be done by replacing the Dot into something else, but that “something else” is your challenge. The hint is: it could have reshape and dot. The output from NN_u and NN_m are two matrices, and the idea is how to rearrange them so that the dot product will operate on all pairs of user and movie. The “how” requires your knowledge in broadcasting which is a topic brought up in Course 1 Week 2 Lab " Python, NumPy and vectorization".

Here are my suggestions:

  1. Read about broadcasting.

  2. Create simple, small, managable numpy arrays resembling the outputs from NN_u and NN_m , then implement reshape, dot and any other things enough for you to produce the desired output matrix with rows representing users and columns movies.

  3. Then you have 2 options:

    1. create a new model with the same architecture except that the Dot is replaced by the “something else”. Then copy learned dense layer parameters from the original model to this new model. This option requires you to explore how to build the “something else” in tensorflow framework, and it is not covered in this specialization.
    2. create a new model by extracting from the original model its inputs up to the outputs of the NN_u and NN_m. This means we cut the model into two and give up the part from “Dot” onwards. Then you may process the outputs from the new model with the numpy version of the “something else” you done in Step 2.

If you have problems with Step 3 but you can show me here your numpy code that passes Step 2, then I can discuss about Step 3 with you.

Good luck,
Raymond

1 Like

Thanks Raymond. I will let you know how I make out.

Hello Raymond. I just wanted to share my end solution with you. It consists of 3 functions:

FUNCTION 1

def my_dense(a_in, W, b):
    """
    Computes dense layer
    Args:
      a_in (ndarray (n,j)) : Data / n = obs / j = # of features
      W    (ndarray (j,k)) : Weight matrix / j = params per node / k = nodes
      b    (ndarray (k,))  : bias vector
    Returns
      a_out (ndarray (n,k)) : prediction for all obs (n) on each node (k)
    """
    z = np.matmul(a_in, W) + b     
    #a_out = relu(z)
    if W.shape[1] > 32:
        a_out = tf.keras.activations.relu(z, alpha=0.0, max_value=None, threshold=0.0).numpy()
    else: 
        a_out = z
    return(a_out)

FUNCTION 2

def my_sequential(X, W1, b1, W2, b2, W3, b3):
    a1 = my_dense(X, W1, b1)
    a2 = my_dense(a1, W2, b2)
    a3 = my_dense(a2, W3, b3)
    return(a3)

FUNCTION 3

def my_predict(X1, X2, model):
    user = model.layers[2].name
    item = model.layers[3].name
    W_c = model.get_layer(user).get_weights()[0:6]
    W_i = model.get_layer(item).get_weights()[0:6]
    
    cp = my_sequential(X1, W_c[0], W_c[1], W_c[2], W_c[3], W_c[4], W_c[5])
    cp = tf.linalg.l2_normalize(cp, axis=1).numpy()
    
    ip = my_sequential(X2, W_i[0], W_i[1], W_i[2], W_i[3], W_i[4], W_i[5])
    ip = tf.linalg.l2_normalize(ip, axis=1).numpy()
    
    ip = ip.T
    
    p = np.matmul(cp,ip)
    
    return(p)

I see. So cp and ip will each produce a matrix where a row represents a customer or an item. Then you transpose one of them and do a matrix multiplication to have all customers to dot with all items. :raised_hands: :raised_hands: :raised_hands:

Hello Dan @danielhopkins80,

I followed your idea of using transpose and made another version that doesn’t need you to copy weights from your trained model, instead it extracts layers from your trained model.

model2_input = model.input
user_embedding_normalized = model.layers[12].output
item_embedding_normalized = model.layers[13].output

model2_output = tf.linalg.matmul(
    user_embedding_normalized,
    tf.transpose(item_embedding_normalized),
)
model2 = tf.keras.Model(model2_input, model2_output)
model2.summary()

To make predictions

model2(
    [user_train[:10, u_s:], item_train[:10, i_s:]]
)

Cheers,
Raymond

Thank you Raymond! I will take a closer look. Super helpful!!