Recommend using Scikit-Learn and Tensorflow Recommender | by Jesko Rehberg | Towards Data Science

Motivation:
We will learn how to recommend sales items to customers, looking at customer’s individual purchase history. In my previous post I explained many concepts of recommending engines in detail:
towardsdatascience.com
This post focuses on recommending using Scikit-Learn and Tensorflow Recommender.
Solution:
First of all, let us have a look at our dataframe (data is stored in my github repository):
import pandas as pdimport numpy as npdata = pd.read_excel(‘/content/gdrive/MyDrive/DDDDFolder/DDDD.xlsx’) data.head()
So what we have got is Sales per Sales Day, Customer (e.g. Customer code 0 is one specific customer) and Sales Item (similar to Customer, Sales Item 0 is one specific Sales Item).
The other columns are not relevant for our product recommendation, so we will simply drop those:
DataPrep = data[['SalesItem', 'SalesAmount', 'Customer']] #we will only use SalesItem, SalesAmount and CustomerDataPrep.head()
We want to see what Sales Items have been purchased by what Customer:
DataGrouped = DataPrep.groupby(['Customer', 'SalesItem']).sum().reset_index() # Group togetherDataGrouped.head()
Our Collaborative Filtering will be based on binary data. For every dataset we will add a 1 as purchased. That means, that this customer has purchased this item, no matter how many the customer actually has purchased in the past. We use this binary data approach for our recommending example. Another approach would be to use the SalesAmount and normalize it, in case you want to treat the Amount of SalesItems purchased as a kind of taste factor, meaning that someone who bought SalesItem x 100 times- while another Customer bought that same SalesItem x only 5 times- does not like it as much. I believe that very often in Sales Recommendations a binary approach makes more sense, but of course that really depends on your data:
def create_DataBinary(DataGrouped):DataBinary = DataGrouped.copy()DataBinary['PurchasedYes'] = 1return DataBinaryDataBinary = create_DataBinary(DataGrouped)DataBinary.head()
Finally, let’s get rid of the column SalesAmount:
purchase_data=DataBinary.drop(['SalesAmount'], axis=1)purchase_data.head()
For better convenience we add I as a prefix for Item for every SalesItem. Otherwise we would only have customer and SalesItem numbers, which can be a little bit puzzling:
purchase_data[‘SalesItem’] = ‘I’ + purchase_data[‘SalesItem’].astype(str)
Now our dataframe is eventually prepared for building the recommender. Why all the data munging you might ask? The reason for this is I hope it helps you to adopt this example to your data, especially if you are a newbie to this.
Now we will import the necessary libraries for recommending with Scikit-Learn:
from scipy.sparse import coo_matrix, csr_matrixfrom sklearn.metrics.pairwise import cosine_similarityfrom sklearn.preprocessing import LabelEncoder
Let’s now calculate the Item-Item cosine similarity:
def GetItemItemSim(user_ids, product_ids):SalesItemCustomerMatrix = csr_matrix(([1]*len(user_ids), (product_ids, user_ids)))similarity = cosine_similarity(SalesItemCustomerMatrix)return similarity, SalesItemCustomerMatrix
Receiving the top 10 SalesItem recommendations per Customer in a dataframe, we will use the Item-Item Similarity Matrix from above cell via creating a SalesItemCustomerMatrixs (SalesItems per rows and Customer as columns filled binary incidence).
def get_recommendations_from_similarity(similarity_matrix, SalesItemCustomerMatrix, top_n=10):CustomerSalesItemMatrix = csr_matrix(SalesItemCustomerMatrix.T)CustomerSalesItemScores = CustomerSalesItemMatrix.dot(similarity_matrix) # sum of similarities to all purchased productsRecForCust = []for user_id in range(CustomerSalesItemScores.shape[0]):scores = CustomerSalesItemScores[user_id, :]purchased_items = CustomerSalesItemMatrix.indices[CustomerSalesItemMatrix.indptr[user_id]:CustomerSalesItemMatrix.indptr[user_id+1]]scores[purchased_items] = -1 # do not recommend already purchased SalesItemstop_products_ids = np.argsort(scores)[-top_n:][::-1]recommendations = pd.DataFrame(top_products_ids.reshape(1, -1),index=[user_id],columns=['Top%s' % (i+1) for i in range(top_n)])RecForCust.append(recommendations)return pd.concat(RecForCust)
Compute the recommendations:
def get_recommendations(purchase_data):user_label_encoder = LabelEncoder()user_ids = user_label_encoder.fit_transform(purchase_data.Customer)product_label_encoder = LabelEncoder()product_ids = product_label_encoder.fit_transform(purchase_data.SalesItem)# compute recommendationssimilarity_matrix, SalesItemCustomerMatrix = GetItemItemSim(user_ids, product_ids)recommendations = get_recommendations_from_similarity(similarity_matrix, SalesItemCustomerMatrix)recommendations.index = user_label_encoder.inverse_transform(recommendations.index)for i in range(recommendations.shape[1]):recommendations.iloc[:, i] = product_label_encoder.inverse_transform(recommendations.iloc[:, i])return recommendations
Let’s start our recommender:
recommendations = get_recommendations(purchase_data)
That means for instance, that Customer 0 (first row in above screenshot) should be highly interested in buying Item I769, second most relevant I253, top 3 I1146 etc.
In case you want to export the recommendations to e.g. excel, you could do like that:
dfrec = recommendationsdfrec.to_excel("ExportCustomerName-Itemname.xlsx")
We are now finished with recommending Sales Items to Customers which they should be highly interested in (but have not already purchased), using Scikit-Learn.
Tensorflow Recommender:
Out of curiosity, let’s repeat this, this time using Tensorflow Recommender. The original code [1] has been taken from Google’s Brain Team and has only been slightly adopted to our data set. I ran the code in Google Colab:
!pip install -q tensorflow-recommenders!pip install -q --upgrade tensorflow-recommendersfrom typing import Dict, Textimport numpy as npimport tensorflow as tfimport tensorflow_recommenders as tfrs
We will also add C (for Customer) as a prefix to every customer id:
purchase_data['Customer'] = 'C' + purchase_data['Customer'].astype(str)uniq = data.SalesItem.unique()uniq = pd.DataFrame(uniq)uniq.columns = ['SalesItem']uniqrat = purchase_data[['Customer', 'SalesItem', 'SalesAmount']]dataset = tf.purchase_data.Dataset.from_tensor_slices(dict(purchase_data))ratings = dataset.from_tensor_slices(dict(rat))SalesItem = dataset.from_tensor_slices(dict(uniq))ratings = ratings.map(lambda x: {"Customer": x["Customer"],"SalesItem": x["SalesItem"]})SalesItem = SalesItem.map(lambda x: x["SalesItem"])ratings.take(1)CustomerID_vocabulary = tf.keras.layers.experimental.preprocessing.StringLookup(mask_token=None)CustomerID_vocabulary.adapt(ratings.map(lambda x: x["Customer"]))SalesItem_vocabulary = tf.keras.layers.experimental.preprocessing.StringLookup(mask_token=None)SalesItem_vocabulary.adapt(SalesItem)#Define a model#We can define a TFRS model by inheriting from tfrs.Model and implementing the compute_loss method:class SalesItemRecModel(tfrs.Model):def __init__(self,CustomerModel: tf.keras.Model,SalesItemModel: tf.keras.Model,task: tfrs.tasks.Retrieval):super().__init__()# Set up Customer and SalesItem representations.self.CustomerModel = CustomerModelself.SalesItemModel = SalesItemModel# Set up a retrieval task.self.task = taskdef compute_loss(self, features: Dict[Text, tf.Tensor], training=False) -> tf.Tensor:# Define how the loss is computed.CustEmbeddings = self.CustomerModel(features["Customer"])SalesItemEmbeddings = self.SalesItemModel(features["SalesItem"])return self.task(CustEmbeddings, SalesItemEmbeddings)
Define Customer and SalesItem models:
CustomerModel = tf.keras.Sequential([CustomerID_vocabulary,tf.keras.layers.Embedding(CustomerID_vocabulary.vocab_size(), 64)])SalesItemModel = tf.keras.Sequential([SalesItem_vocabulary,tf.keras.layers.Embedding(SalesItem_vocabulary.vocab_size(), 64)])task = tfrs.tasks.Retrieval(metrics=tfrs.metrics.FactorizedTopK(SalesItem.batch(128).map(SalesItemModel)))
Now we will create the model, train it, and generate predictions:
model = SalesItemRecModel(CustomerModel, SalesItemModel, task)model.compile(optimizer=tf.keras.optimizers.Adagrad(0.5))# Train for 3 epochs.model.fit(ratings.batch(4096), epochs=3)# Use brute-force search to set up retrieval using the trained representations.index = tfrs.layers.factorized_top_k.BruteForce(model.CustomerModel)index.index(SalesItem.batch(100).map(model.SalesItemModel), SalesItem)customers = data.Customer.unique().tolist()fcst = pd.DataFrame()for x in customers:_, SalesItem = index(np.array([x]))fcst = pd.concat((fcst, pd.DataFrame(SalesItem[0, :10].numpy()).transpose()))fcst['Customer'] = customersfcst.to_excel('RecFc.xlsx', index=False)
Hope this was supportive. Any questions, please let me know. You can connect with me on LinkedIn or Twitter. Many thanks for reading!
Originally published on my website DAR-Analytics, where also the Jupyter Notebook and data file is available.
Reference: [1] Maciej Kula and James Chen, Google Brain, Introducing TensorFlow Recommenders (2020), https://blog.tensorflow.org/2020/09/introducing-tensorflow-recommenders.html