LibRecommender icon indicating copy to clipboard operation
LibRecommender copied to clipboard

How to server DeepFM or YouTubeRanking ?

Open batrlatom opened this issue 1 year ago • 4 comments

Hi, I have tried to train and deploy simplest model possible with csv structure like this : item | user | label I am trying to save the model, but I am receiving this error after training is done...

  File "/recommender/libreco/utils/serialization.py", line 53, in save_info
    save_to_json(last_interacted_path, model,
  File "/recommender/libreco/utils/serialization.py", line 58, in save_to_json
    json_data = convert_func(data)
  File "/recommender/libreco/utils/serialization.py", line 97, in convert_last_interacted_to_json
    seq_len = model.max_seq_len
AttributeError: 'YouTubeRanking' object has no attribute 'max_seq_len'

How to make the model saving and running? I am able to do only DIN model and even that worked only with your example code. When I tried to change the model for DeepFM, i was not able to do it.

The code is:

import numpy as np
import pandas as pd
from libreco.data import split_by_ratio, DatasetFeat
from libreco.algorithms import DeepFM, YouTubeRanking
from libreco.utils import save_info, save_model_tf_serving
from serving.flask import data_info2redis, user_consumed2redis, seq2redis


###############################################################
data = pd.read_csv("mydata.csv", sep=",", header=0)
data["label"] = 1  

train_data, test_data = split_by_ratio(data, test_size=0.2)

sparse_col = []
dense_col = []
user_col = []
item_col = []

train_data, data_info = DatasetFeat.build_trainset(
    train_data, user_col, item_col, sparse_col, dense_col
)

test_data = DatasetFeat.build_testset(test_data)
train_data.build_negative_samples(data_info)  # sample negative items for each record
test_data.build_negative_samples(data_info)

model = YouTubeRanking(task="ranking", data_info=data_info, embed_size=16,
                             n_epochs=10, lr=3e-5, batch_size=128, use_bn=True,
                             hidden_units="128, 64,32")

model.fit(train_data, verbose=2, shuffle=True, eval_data=test_data,
                metrics=["loss", "roc_auc", "precision", "recall", "map", "ndcg"])


path = "tf_model"  # specify model saving directory
save_info(path, model, train_data, data_info) 
save_model_tf_serving(path, model, "test_mymodel")
data_info2redis(path)
user_consumed2redis(path)  consumed
seq2redis(path) 

batrlatom avatar Jul 28 '22 15:07 batrlatom

change interaction_num to max_seq_len in youtube_ranking.py

sunithak avatar Aug 08 '22 09:08 sunithak

Yeah YoutubeRanking's attribute is wrong, which will be fixed in the next version. DeepFM doesn't use item sequence as DIN does, and the doc hasn't been updated to adapt to that. Considering refactoring the serving module.

massquantity avatar Aug 09 '22 18:08 massquantity

Hi, I have solved the problem by going around :] with fastapi, uvicorn and saving/loading the model directly. Probably not so fast, but good enough for me now.

# model_serve.py
import string
from typing import Tuple, Union

from fastapi import FastAPI
import pandas as pd
from fastapi.middleware.cors import CORSMiddleware

from libreco.data import split_by_ratio, DatasetFeat
from libreco.algorithms import YouTubeRanking, DeepFM, DIN
from libreco.data.data_info import DataInfo
from libreco.utils import save_info, save_model_tf_serving
import os
import tensorflow as tf
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
os.environ["KMP_WARNINGS"] = "FALSE"
tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.ERROR)

origins = [    
    "http://0.0.0.0:3000",
    "http://localhost:3000"
]

def load(model_path = "mymodel"):
    tf.compat.v1.reset_default_graph()
    data_info = DataInfo.load(model_path)
    model = DeepFM.load(path=model_path, model_name="deepfm",
                        data_info=data_info, manual=True)
    return model
mymodel = load()

app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
def read_root():
    return {"Hello": "World"}

@app.get("/id/{user_id}")
def read_item(user_id, q: Union[str, None] = None):
    out = mymodel.recommend_user(user=user_id, n_rec=12, cold_start="popular")
    print(type(out[0]))
    print(out)

    items = []
    
    if type(out[0]) is tuple:
        for index, data in enumerate(out):
            items.append(data[0])
    else:
        items = out
    return {"items_id": items}

run serve_model.py with

uvicorn serve_model:app

batrlatom avatar Aug 09 '22 19:08 batrlatom

ok glad it works :)

massquantity avatar Aug 09 '22 19:08 massquantity