File size: 5,228 Bytes
b3f3132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56f7ba8
b3f3132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import streamlit as st
from ranker import SparseTfIdfRanker, BertRanker, SparseDenseMoviesRanker
import pandas as pd
import torch
from utils import get_image_from_url

st.set_page_config(layout="wide")

@st.cache(allow_output_mutation = True)
def get_ranker(modele):
    df = pd.read_pickle('films.pkl')
    
    if modele=="sparse-dense":
        modelpath = "sentence-transformers/multi-qa-MiniLM-L6-cos-v1"
        bert_index = torch.load('multi-qa-MiniLM-L6-cos-v1_embeddings.pth')
        sparse_index = torch.load('sparsefake.pth')
        ranker = SparseDenseMoviesRanker(df, modelpath=modelpath, bert_index = bert_index, sparse_index = sparse_index, vectorizer_path='my_v.pkl')
    elif modele=="sparse":
        sparse_index = torch.load('sparsefake.pth')
        ranker = SparseTfIdfRanker(df, vectorizer_path = 'my_v.pkl', index_matrix = sparse_index)
    elif modele=="dense":
        modelpath = "sentence-transformers/multi-qa-MiniLM-L6-cos-v1"
        bert_index = torch.load('multi-qa-MiniLM-L6-cos-v1_embeddings.pth')
        ranker = BertRanker(df, index_matrix = bert_index, modelpath=modelpath)
    else:
        return NotImplementedError
    return ranker


def main():

    st.title("Canap' _is all you need_ 🎥 ") 
    st.header("Prototype simple de recommandation de contenus basée sur des description de films")
    st.write("Jeunes parents, je m'adresse en particulier à vous. Pendant que vos jeunes chérubins 👶 tentent de trouver le sommeil,  la plupart de vos soirées se terminent dans les tréfonds d'un canapé, emportés par algorithme de recommandation de contenu qui se base sur moult critères. D'un autre côté, les choix proposés par les sites francophones vous font rentrer dans un vortex sans fin qui allégera vos espoirs d'une soirée cinéma réussie.")
    st.write("Je vous propose, avec cette maquette, de comprendre un tout petit peu ce qui se passe derrière le capot.") 
    st.write("⚠️  Il s'agit bien évidemment d'un prototype; je rejette toute responsabilité quant au potentiel ratage d'un dîner pour cause de promesse cinématographique non tenue ⚠️ . Après tout, je ne suis qu'un ingénieur. Si j'étais critique ciné, je ne connaîtrais probablement pas [Hugging Face](https://huggingface.co/) 🤗, ce qui serait bien dommage - et je connais déjà Martin Scorsese, ce qui suffit amplement à me pavaner en soirée (même si, en tant que jeune parent, je n'ai plus de soirée, souvenez-vous).")
    st.subheader("Principe et objectifs")
    st.write("On propose ici de comparer trois modèles de recommandation sur une base de films.")
    st.write("Plus sérieusement, l'objectif est ici triple")
    st.write("* Montrer que l'on peut rapidement prototyper un modèle de recommandation sur base de contenus") 
    st.write("* Prouver qu'il est également possible de le faire en langue française. Et, plus généralement, encourager les avancées remarquables du NLP francophone")
    st.write("* Avec la mise à disposition des modèles pré-entraînés, la période actuelle de l'intelligence artificielle est similaire à l'avènement de l'open source. Il s'agit juste de dépasser rapidement la phase de prototypage !")
    st.write("Pour les curieux, je suis en train d'élaborer [un post explicatif, plus technique](https://mnemlaghi.github.io/simsearch)")

    st.subheader("La donnée")
    st.write("La donnée indexée est issue de la collecte de descriptifs d'environ 8000 films. Les métadonnées telles que l'image du film sont récupérées en temps réel lors de la requête")

    st.subheader("Expérimentons !")
    modele = st.selectbox("Choisissez votre modèle de recommandation", ['sparse-dense', 'sparse', 'dense'])
    ranker = get_ranker(modele)
    query = st.text_input("Quelle histoire voulez-vous regarder ce soir ?", "Une histoire de pirates à la recherche d'un trésor")
    topn = st.number_input("Combien de films souhaitez-vous afficher ?", min_value = 1, max_value = 20, value = 5)


    with st.spinner("Canap' vous recherche des films appropriés sur une base de plus de 8000 films..."):
        if modele=='sparse-dense':
            user_firstranking = st.number_input("Filtre isssu du premier ranking (vous pouvez le laisser par défaut à 1000) ?", min_value = 10, max_value = 1000, value=1000)
            df = ranker.run_query(query, topn, first_ranking=user_firstranking)
        else:
            df = ranker.run_query(query, topn)
    
    score_key = 'tfidf-score' if modele=='sparse' else 'bert-score'
    if st.button("Qu'est-ce qu'on regarde, ce soir ?"):
        for i,v in df.iterrows():
            url, desc, score, title = v['url'], v['desc'], v[score_key], v['title']
            st.header(title)
            col1, col2, col3 = st.columns([3,5,2])
            with col1:
                st.image(get_image_from_url(url))
                st.write(f"[Lien]({url})")

            with col2:
                desc_low = desc[:50]
                with st.expander(desc_low+'...'):
                    st.write(desc)
            
            with col3:
                percent = str(round(score*100, 1)) +"%"
                st.metric('similarité', percent)

if __name__=='__main__':
    main()