Sistema de recomendación de vinos con transfer learning y búsqueda semántica

Sistema de recomendación de vinos con transfer learning y búsqueda semántica

Francisco Espiga
Mayo, 2021

Más sobre ciencia de datos: cienciadedatos.net

Introducción


En este artículo me he propuesto aunar dos de mis pasiones, el vino y la ciencia de datos, para poder encontrar recomendaciones de nuevos vinos basadas en opiniones de críticos. Para ello, hago uso de métodos de transfer learning y búsqueda semántica.

Los datos se encuentran disponibles en https://www.kaggle.com/zynicide/wine-reviews de manera abierta y contienen reseñas de más de 120.000 vinos diferentes del viejo y nuevo mundo, procedentes de la web Wine Enthusiast.

In [1]:
import pandas as pd
wine_data = pd.read_csv('./winemag-data-130k-v2.zip')
wine_data = wine_data.drop(columns='Unnamed: 0')
wine_data.head()
Out[1]:
country description designation points price province region_1 region_2 taster_name taster_twitter_handle title variety winery
0 Italy Aromas include tropical fruit, broom, brimston... Vulkà Bianco 87 NaN Sicily & Sardinia Etna NaN Kerin O’Keefe @kerinokeefe Nicosia 2013 Vulkà Bianco (Etna) White Blend Nicosia
1 Portugal This is ripe and fruity, a wine that is smooth... Avidagos 87 15.0 Douro NaN NaN Roger Voss @vossroger Quinta dos Avidagos 2011 Avidagos Red (Douro) Portuguese Red Quinta dos Avidagos
2 US Tart and snappy, the flavors of lime flesh and... NaN 87 14.0 Oregon Willamette Valley Willamette Valley Paul Gregutt @paulgwine Rainstorm 2013 Pinot Gris (Willamette Valley) Pinot Gris Rainstorm
3 US Pineapple rind, lemon pith and orange blossom ... Reserve Late Harvest 87 13.0 Michigan Lake Michigan Shore NaN Alexander Peartree NaN St. Julian 2013 Reserve Late Harvest Riesling ... Riesling St. Julian
4 US Much like the regular bottling from 2012, this... Vintner's Reserve Wild Child Block 87 65.0 Oregon Willamette Valley Willamette Valley Paul Gregutt @paulgwine Sweet Cheeks 2012 Vintner's Reserve Wild Child... Pinot Noir Sweet Cheeks

Descripción


En muchas ocasiones, encontramos vinos sorprendentes y fuera de lo que estamos acostumbrados y nos apetecería probar cosas similares. Ya sea por el terroir, la variedad de uva o la puntuación en listas reputadas, podemos identificar nuevos candidatos gracias a ello, pero también sería fantástico poder tener en cuenta el conocimiento de expertos críticos a la hora de tomar nuestras elecciones.

Para ello, voy a utilizar las notas de cata como punto de partida y a transformarlas mediante NLP en vectores representativos de toda la descripción, o sentence embeddings para comparar unos vinos con otros.

Breve análisis exploratorio


Para verificar que las recomendaciones van a ser diversas, he escogido el top 10 tanto de países como de variedades de uva. Observamos que, si bien Estados Unidos es el país predominante de origen de los vinos del set de datos, lo compensamos con los mayores productores del viejo mundo (Italia, Francia y España) por lo que obtendremos recomendaciones de ambos lados del Atlántico para explorar.

En lo referente a los varietales, contamos tanto con blanca como tinta, por lo que del mismo modo que con el origen, no estaremos sesgados ante un tipo u otro de vino.

In [2]:
wine_data.groupby('country').count().sort_values('description').tail(10).plot.bar(y ='description');
In [3]:
wine_data.groupby('variety').count().sort_values('description').tail(10).plot.bar(y ='description');

Enfoque teórico


Transfer learning para generar embeddings de las notas de cata


En primer lugar, vamos a utilizar un modelo preentrenado (transfer learning), para convertir las notas de cata, que esrtán en formato texto, a vectores numéricos que nos permitan encontrar recomendaciones.

Para ello, usaremos el Google Universal Sentence Encoder, que está disponible en el TensorFlow hub y que convierte cualquier frase en inglés en un vector de 512 dimensiones o sentence embedding.

En caso de tener las descripciones en varios idiomas, podríamos recurrir a alternativas multilenguaje, aunque no es el caso que nos ocupa, ya que todas están en inglés. Como es un proceso costoso, almacenaremos los resultados de transformar cada nota en un tensor con tantas observaciones como vinos y 512 columnas correspondientes a las dimensiones del embedding.

In [4]:
import cloudpickle
from tqdm import tqdm
import tensorflow_hub as hub
import numpy as np
from sklearn.manifold import TSNE
import tensorflow as tf 
import math 
In [5]:
create_embeddings = False

if create_embeddings:
    use = hub.load("https://tfhub.dev/google/universal-sentence-encoder/4")
    
    # Raw embeddings
    EMBEDDINGS = [] 
    
    for i in tqdm(wine_data['description'].tolist()):
        EMBEDDINGS.append(use([i]))
    
    EMBEDDINGS = np.array(EMBEDDINGS)
    EMBEDDINGS = EMBEDDINGS.reshape([EMBEDDINGS.shape[0], EMBEDDINGS.shape[2]])
    
    cloudpickle.dump(EMBEDDINGS, open('./wine_embeddings.p','wb'))
else:
    EMBEDDINGS = cloudpickle.load(open('./wine_embeddings.p','rb'))
    EMBEDDINGS = np.array(EMBEDDINGS)
    EMBEDDINGS = EMBEDDINGS.reshape([EMBEDDINGS.shape[0], EMBEDDINGS.shape[2]])

Podemos comprobar que los embeddings están normalizados. Esto es importante de cara a calcular su similitud para obtener recomendaciones.

In [6]:
sum(EMBEDDINGS[0,:]**2)
Out[6]:
1.000000010430318

Usando la similitud del coseno para obtener recomendaciones.


Una vez transformadas todas las notas de cata en vectores, podemos utilizar la similitud del coseno para obtener aquellos vectores más cercanos a uno dado usando la siguiente fórmula:

$$ Similitud(A, B) = \frac{A \cdot B}{||A||\cdot||B||} $$

En este caso, nosotros lo transformaremos en una puntuación siguiendo las indicaciones para similitud textual del benchmark STS.

In [7]:
def compute_scores(a, b):
    a = tf.nn.l2_normalize(a, axis=1)
    b = tf.nn.l2_normalize(b, axis=1)
    cosine_similarities = tf.matmul(a, b, transpose_b = True)
    clip_cosine_similarities = tf.clip_by_value(cosine_similarities, -1.0, 1.0)
    scores = 1.0 - tf.acos(clip_cosine_similarities) / math.pi
    return scores

def get_recommendations(query, embeddings, top_k = 10):
    if len(query.shape)==1:
        query = tf.expand_dims(query, 0)
    sim_matrix = compute_scores(query, embeddings)
    rank = tf.math.top_k(sim_matrix, top_k)
    return rank

Vamos a tomar un ejemplo aleatorio, la muestra número 15, que corresponde a un Riesling de Alemania. Comprobamos que las primeras recomendaciones son coherentes ya que, o bien son de la misma variedad y origen, o de la región de Alsacia, en Francia.

También encontramos recomendaciones interesantes, como un Riesling estadounidense, un rosado alsaciano con Pinot Noir y un Chardonnay austríaco.

In [8]:
scores, rank = get_recommendations(EMBEDDINGS[15,:], EMBEDDINGS, top_k = 10)
wine_data.iloc[rank.numpy().reshape(-1).tolist(), :]
Out[8]:
country description designation points price province region_1 region_2 taster_name taster_twitter_handle title variety winery
15 Germany Zesty orange peels and apple notes abound in t... Devon 87 24.0 Mosel NaN NaN Anna Lee C. Iijima NaN Richard Böcking 2013 Devon Riesling (Mosel) Riesling Richard Böcking
121561 Germany Whiffs of freshly pressed apples are accented ... Trocken 90 30.0 Rheinhessen NaN NaN Anna Lee C. Iijima NaN Prinz Salm 2012 Trocken Riesling (Rheinhessen) Riesling Prinz Salm
16732 France Lovely red apple, ripe pear and mirabelle plum... Cuvée Engel 90 22.0 Alsace Alsace NaN Anne Krebiehl MW @AnneInVino Domaine Fernand Engel 2015 Cuvée Engel Gewurzt... Gewürztraminer Domaine Fernand Engel
118268 France Juicy apple notes, both ripe and tart, make fo... Clos Häuserer 93 60.0 Alsace Alsace NaN Anne Krebiehl MW @AnneInVino Domaine Zind-Humbrecht 2014 Clos Häuserer Ries... Riesling Domaine Zind-Humbrecht
86357 Germany Smoke and pressed apple notes are savory on th... Nik Weis Wiltinger Alte Reben 89 18.0 Mosel NaN NaN Anna Lee C. Iijima NaN St. Urbans-Hof 2013 Nik Weis Wiltinger Alte Re... Riesling St. Urbans-Hof
65689 US Waxy lemon peel accents savory apple and quinc... Dry 86 16.0 New York New York New York Other Anna Lee C. Iijima NaN Suhru 2012 Dry Riesling (New York) Riesling Suhru
70379 France Baked red currant and apple notes create a ver... Clérostein Rosé 89 15.0 Alsace Alsace NaN Anne Krebiehl MW @AnneInVino Cave de Cleebourg NV Clérostein Rosé Pinot Noi... Pinot Noir Cave de Cleebourg
12657 Austria Friendly, ripe green and yellow apple notes ap... NaN 90 20.0 Carnuntum NaN NaN Anne Krebiehl MW @AnneInVino Netzl 2016 Chardonnay (Carnuntum) Chardonnay Netzl
60729 France Both citrus and apple notes are dulled by very... Réserve 84 21.0 Alsace Crémant d'Alsace NaN Anne Krebiehl MW @AnneInVino Pierre Sparr NV Réserve Sparkling (Crémant d'A... Sparkling Blend Pierre Sparr
14637 US Curious hints of candied citrus peel, dried fi... Tanzen Dame Auten Vineyard 2nd Harvest 90 60.0 New York Finger Lakes Finger Lakes Anna Lee C. Iijima NaN Bloomer Creek 2012 Tanzen Dame Auten Vineyard ... Riesling Bloomer Creek

Recomendaciones generales


En el caso de querer buscar recomendaciones similares a un vino específico, es sencillo obtener candidatos por similitud, pero, ¿qué sucede en el caso de que sean más genéricas, no respecto a un vino concreto, sino a una región o variedad?

En este caso, lo que podemos hacer es considerar los vectores de todos aquellos vinos que cumplen las características y promediarlos para obtener recomendaciones próximas a todos ellos. Ese vector promedio representa a un "candidato arquetípico" del cuál buscaremos recomendaciones cercanas.

Vamos ahora a inspeccionar esta casuística, para evaluar el comportamiento del recomendador ante:

  • Un vino tinto australiano con uva Pinot Noir, buscando recomendaciones del viejo mundo.

  • Un vino tinto de la D.O.Ca. Rioja restringiendo las recomendaciones a no ser españolas.

  • Vinos similiares al Prosecco que no procedan ni de Francia ni de Italia.

Tinto australiano Pinot Noir

Generamos el embedding promedio de todos los vinos candidatos. Comprobamos que obtenemos resultados interesantes, la mayoría del nuevo mundo y de la misma variedad de uva, aunque también sorprendentes como un Meinklang 2015 austríaco, de uva Blauburgunder (otro apelativo para la Pinot Noir).

In [9]:
indices = wine_data[(wine_data['country'] == 'Australia')&(wine_data['variety'] == 'Pinot Noir')].index
In [10]:
australian_pinotnoir = tf.math.reduce_mean(EMBEDDINGS[indices,:], axis = 0)
In [11]:
scores, rank = get_recommendations(australian_pinotnoir, EMBEDDINGS, top_k = 10)
wine_data.iloc[rank.numpy().reshape(-1).tolist(), :]
Out[11]:
country description designation points price province region_1 region_2 taster_name taster_twitter_handle title variety winery
3459 Australia Pretty aromas of cherry and plum blossoms give... The Dagger 88 21.0 South Australia Adelaide Hills NaN Joe Czerwinski @JoeCz Riposte 2015 The Dagger Pinot Noir (Adelaide H... Pinot Noir Riposte
73039 France This wine proclaims itself with forward and ni... Cumulo Nimbus 91 35.0 Languedoc-Roussillon Minervois NaN NaN NaN Abbotts 1998 Cumulo Nimbus Shiraz (Minervois) Shiraz Abbotts
33796 Austria Juicy, vivid black cherry fruit appears on the... NaN 93 19.0 Burgenland NaN NaN Anne Krebiehl MW @AnneInVino Meinklang 2015 Blauburgunder (Burgenland) Blauburgunder Meinklang
61723 US An estate-grown Pinot Noir from the wilds of F... Pastorale Vineyard Estate Grown 93 75.0 California Sonoma Coast Sonoma Virginie Boone @vboone Joseph Phelps 2012 Pastorale Vineyard Estate G... Pinot Noir Joseph Phelps
31013 US Fragrant on the nose, with hints of black cher... Estate Grown 87 40.0 New York North Fork of Long Island Long Island Anna Lee C. Iijima NaN Sherwood House Vineyards 2010 Estate Grown Cab... Cabernet Franc Sherwood House Vineyards
70895 US This pure varietal blend of Merlot opens fragr... NaN 90 29.0 Washington Columbia Valley (WA) Columbia Valley Paul Gregutt @paulgwine Beresan 2006 Merlot (Columbia Valley (WA)) Merlot Beresan
124796 US Hints of smoke and vanilla add complexity to n... Reserve 88 30.0 New York Finger Lakes Finger Lakes Anna Lee C. Iijima NaN Shaw 2008 Reserve Pinot Noir (Finger Lakes) Pinot Noir Shaw
12347 US Fresh black cherry and plum aromas are pristin... Oregon Road 88 18.0 New York North Fork of Long Island Long Island Anna Lee C. Iijima NaN Sherwood House Vineyards 2010 Oregon Road Merl... Merlot Sherwood House Vineyards
67519 Romania This bargain Pinot Noir is a pleasant surprise... Reserve 87 10.0 Dealu Mare NaN NaN Anna Lee C. Iijima NaN Prahova Valley 2008 Reserve Pinot Noir (Dealu ... Pinot Noir Prahova Valley
126723 New Zealand A blend of fruit drawn from the Awatere and Br... NaN 90 20.0 Marlborough NaN NaN Joe Czerwinski @JoeCz Jules Taylor 2010 Pinot Noir (Marlborough) Pinot Noir Jules Taylor

D.O.Ca. Rioja

In [12]:
indices = wine_data[(wine_data['region_1'] == 'Rioja')&([str(i).find('Tempranillo')>-1 for i in wine_data['variety'].values.tolist()])].index
In [13]:
spanish_rioja_tempranillo = tf.math.reduce_mean(EMBEDDINGS[indices,:], axis = 0)
In [14]:
scores, rank = get_recommendations(spanish_rioja_tempranillo, EMBEDDINGS, top_k = 1000)
recommendations = wine_data.iloc[rank.numpy().reshape(-1).tolist(), :]
recommendations[recommendations.country != 'Spain'].head(10)
Out[14]:
country description designation points price province region_1 region_2 taster_name taster_twitter_handle title variety winery
6856 Argentina Spicy, slightly green aromas of plum and curra... Monteagrelo 89 27.0 Mendoza Province Mendoza NaN Michael Schachner @wineschach Bressia 2014 Monteagrelo Cabernet Franc (Mendoza) Cabernet Franc Bressia
104662 Chile Bright cherry and berry aromas benefit from ni... Terroir Selection Gran Reserva 91 21.0 Cachapoal Valley NaN NaN Michael Schachner @wineschach Casas del Toqui 2013 Terroir Selection Gran Re... Syrah Casas del Toqui
25111 France This is a direct but tasty and well-balanced P... NaN 87 7.0 France Other Vin de France NaN Lauren Buzzeo @laurbuzz Le Pépin 2015 Pinot Noir (Vin de France) Pinot Noir Le Pépin
119136 France This is a direct but tasty and well-balanced P... NaN 87 7.0 France Other Vin de France NaN Lauren Buzzeo @laurbuzz Le Pépin 2015 Pinot Noir (Vin de France) Pinot Noir Le Pépin
122777 Argentina Rubbery, lightly spicy aromas of plum and berr... NaN 85 10.0 Mendoza Province Tupungato NaN Michael Schachner @wineschach Tupun 2013 Malbec (Tupungato) Malbec Tupun
68850 Argentina Saucy plum and cherry aromas are respectable b... Selección 86 13.0 Mendoza Province Mendoza NaN Michael Schachner @wineschach Valle Austral 2016 Selección Malbec (Mendoza) Malbec Valle Austral
13378 Argentina Ripe plum and berry aromas are wrapped in a be... El Alto Parcel 92 100.0 Mendoza Province Luján de Cuyo NaN Michael Schachner @wineschach Doña Paula 2011 El Alto Parcel Malbec (Luján d... Malbec Doña Paula
40859 US Intensely ripe and ruddy black cherry and berr... Estate 87 30.0 New York North Fork of Long Island Long Island Anna Lee C. Iijima NaN Jamesport 2013 Estate Cabernet Franc (North Fo... Cabernet Franc Jamesport
81039 Argentina Smoky cherry and earth aromas are the opening ... Barda 90 30.0 Other Patagonia NaN Michael Schachner @wineschach Bodega Chacra 2011 Barda Pinot Noir (Patagonia) Pinot Noir Bodega Chacra
8640 France Medium-intense scents of ripe blackberry and r... Vieilles Vignes 89 35.0 Languedoc-Roussillon Côtes du Roussillon Villages NaN Lauren Buzzeo @laurbuzz Domaine du Clos des Fées 2009 Vieilles Vignes ... Rhône-style Red Blend Domaine du Clos des Fées

Prosecco

In [15]:
indices = wine_data[([str(i).find('Prosecco')>-1 for i in wine_data['variety'].values.tolist()])].index
prosecco = tf.math.reduce_mean(EMBEDDINGS[indices,:], axis = 0)
In [16]:
scores, rank = get_recommendations(prosecco, EMBEDDINGS, top_k = 1000)
recommendations = wine_data.iloc[rank.numpy().reshape(-1).tolist(), :]
recommendations[(recommendations.country!='Italy')&(recommendations.country!='France')].head(10)
Out[16]:
country description designation points price province region_1 region_2 taster_name taster_twitter_handle title variety winery
72943 US Crisp and light with a sticky mouthfeel that f... Estate Vineyard 91 25.0 California Sonoma County Sonoma Virginie Boone @vboone Wellington 2013 Estate Vineyard Roussanne (Son... Roussanne Wellington
26825 US This intensely aromatic, off-dry Riesling jump... Estate Bottled 87 17.0 New York North Fork of Long Island Long Island Anna Lee C. Iijima NaN Pindar Vineyards 2012 Estate Bottled Riesling ... Riesling Pindar Vineyards
87642 US A blend of Roussanne, Viognier, Grenache Blanc... Slice of Pape Blanc 90 30.0 Washington Columbia Valley (WA) Columbia Valley Sean P. Sullivan @wawinereport Tranche 2013 Slice of Pape Blanc White (Columb... Rhône-style White Blend Tranche
74185 US Gorgeous on the nose with aromas of honeyed ap... Semi-Dry 87 13.0 New York Finger Lakes Finger Lakes Anna Lee C. Iijima NaN Fulkerson 2009 Semi-Dry Riesling (Finger Lakes) Riesling Fulkerson
6241 Germany While richly textured and boldly perfumed with... Hochheimer Domdechaney Trocken Gold Cap 90 47.0 Rheingau NaN NaN Anna Lee C. Iijima NaN Domdechant Werner 2013 Hochheimer Domdechaney ... Riesling Domdechant Werner
92106 Germany Opulent white blossom and tropical fruit flavo... S Bopparder Hamm Feuerlay Trocken 91 29.0 Mittelrhein NaN NaN Anna Lee C. Iijima NaN Matthias Müller 2012 S Bopparder Hamm Feuerlay... Riesling Matthias Müller
100070 South Africa There's great vibrancy on the nose of this win... Unoaked 87 12.0 Stellenbosch NaN NaN Lauren Buzzeo @laurbuzz Winery of Good Hope 2011 Unoaked Chardonnay (S... Chardonnay Winery of Good Hope
15924 Germany Stony mineral notes accent fragrant blossom an... Wachenheimer Belz Dry 91 26.0 Pfalz NaN NaN Anna Lee C. Iijima NaN Villa Wolf 2012 Wachenheimer Belz Dry Riesling... Riesling Villa Wolf
5275 US Beautifully floral on the nose with hints of f... Riesling 10 Hanging Delta Vineyard 88 30.0 New York Finger Lakes Finger Lakes Anna Lee C. Iijima NaN Fox Run 2011 Riesling 10 Hanging Delta Vineyar... Riesling Fox Run
110486 US Beautifully floral on the nose with hints of f... Riesling 10 Hanging Delta Vineyard 88 30.0 New York Finger Lakes Finger Lakes Anna Lee C. Iijima NaN Fox Run 2011 Riesling 10 Hanging Delta Vineyar... Riesling Fox Run

Este último caso es interesante porque en el top 10 obtenemos vinos blancos no espumosos. Podríamos argüir que mayoritariamente han sido excluidos con el filtro del país, al no considerar los champagnes, pero fundamentalmente se debe a que, en las notas de cata, el término sparkling no suele aparecer. Lo verificamos y solo aparece en un 0.6% de las muestras.

In [17]:
spark_indices = [i.find('sparkling')>-1 for i in wine_data.description]
sum(spark_indices) / wine_data.shape[0]
Out[17]:
0.006447592155173077
In [18]:
wine_data[spark_indices & (wine_data.country != 'France') & (wine_data.country != 'Italy')].shape
Out[18]:
(462, 13)
In [19]:
recommendations[(recommendations.country!='Italy')&(recommendations.country!='France')&[str(i).lower().find('sparkling')>-1 for i in recommendations.variety]]
Out[19]:
country description designation points price province region_1 region_2 taster_name taster_twitter_handle title variety winery
54133 England A generously ripe touch of apricot hovers befo... Brut Reserve 93 60.0 England NaN NaN Anne Krebiehl MW @AnneInVino Gusbourne Estate 2013 Brut Reserve Sparkling (... Sparkling Blend Gusbourne Estate
64286 Spain Citrus and yeast aromas form the nose on this ... Cristalino Rosé Brut 84 10.0 Catalonia Cava NaN Michael Schachner @wineschach Jaume Serra NV Cristalino Rosé Brut Sparkling ... Sparkling Blend Jaume Serra
90449 US Exuberant green apple and quince aromas meet a... SP Reserve Brut 90 45.0 Virginia Monticello NaN Alexander Peartree NaN Trump 2008 SP Reserve Brut Sparkling (Monticello) Sparkling Blend Trump
43560 Argentina Peach and flower aromas are pleasant and light... Sweet Gold 84 15.0 Mendoza Province NaN NaN Michael Schachner @wineschach New Age NV Sweet Gold Sparkling Sparkling Blend New Age
7667 US Made from native Cayuga grapes, this softly pé... Sparkling Wine 85 20.0 New York New York New York Other Anna Lee C. Iijima NaN Hosmer NV Sparkling Wine Sparkling (New York) Sparkling Blend Hosmer
13491 US Made from native Cayuga grapes, this softly pé... Sparkling Wine 85 20.0 New York New York New York Other Anna Lee C. Iijima NaN Hosmer NV Sparkling Wine Sparkling (New York) Sparkling Blend Hosmer
105523 England Smooth hints of apricot and vanilla immediatel... Classic Reserve 92 45.0 England NaN NaN Anne Krebiehl MW @AnneInVino Hattingley Valley Wines NV Classic Reserve Spa... Sparkling Blend Hattingley Valley Wines
89316 US A light touch of sweetness combines with ripe ... Mirabelle Brut 90 27.0 California California California Other Jim Gordon @gordone_cellars Mirabelle NV Mirabelle Brut Sparkling (Califor... Sparkling Blend Mirabelle
129621 Ukraine Fresh apple notes on the nose of this off-dry ... KrimSekt White Collection Semi-Dry 84 13.0 Ukraine NaN NaN Anna Lee C. Iijima NaN Artemovsk 2008 KrimSekt White Collection Semi-... Sparkling Blend Artemovsk
112264 England There is almost something of the sea breeze in... Brut Reserve 93 75.0 England NaN NaN Anne Krebiehl MW @AnneInVino Bride Valley Vineyard 2014 Brut Reserve Sparkl... Sparkling Blend Bride Valley Vineyard

Verificamos que aunque hay algunos candidatos de entre los vinos espumosos, esa característica se pierde entre la descripción de la nota de cata y el recomendador se centra en otras características.

Visualizando los resultados


Es interesante poder obtener un listado de recomendaciones dado un determinado terroir, variedad, etc. Pero igualmente interesante el poder explorar de manera interactiva los resultados.

Para ello, usaremos T-SNE para reducir la dimensionalidad de 512 a 2 dimensiones. Escogeremos aleatoriamente 5000 muestras de entre todos los vinos reseñados para que sea visual y computacionalmente más eficaz.

In [20]:
sample_num = 5000
idxs = tf.range(tf.shape(EMBEDDINGS)[0])
ridxs = tf.random.shuffle(idxs)[:sample_num]
rinput = tf.gather(EMBEDDINGS, ridxs)
In [21]:
descriptions = wine_data.iloc[ridxs,:]
descriptions = descriptions.loc[:,['country', 'designation', 'province', 'title',
                                   'variety', 'winery', 'price', 'points']]
In [22]:
# T-SNE 
X_embedded = TSNE(n_components=2).fit_transform(rinput)
In [23]:
import plotly.express as px
fig = px.scatter(pd.DataFrame({'x': X_embedded[:,0], 
                               'y': X_embedded[:,1], 
                               'country':descriptions['country'],
                               'name':descriptions['title'],
                               'variety':descriptions['variety'],
                               'designation':descriptions['designation']
                              
                              }).dropna(), 
                 x='x', y='y', color='country', hover_name="name",
                 hover_data=["variety", "name"],
                 width=700, height=400
                )
fig.show()

Observamos en la proyección en 2 dimensiones usando TSNE cómo los vinos tienden a agruparse por país, con algunos descubrimientos interesantes como las agrupaciones de tintos españoles de Rioja y Toro con tintos de Argentina y Chile, igualemente intensos o la cercanía entre los vinos portugueses de Dão y franceses.

Siguientes pasos


Para poder sacar partido a toda la información disponible, sería interesante tanto disponibilizar un motor de búsqueda más refinado y que permita a los usuarios introducir filtros complejos, como visualizar el grafo de similaridad y poder colorearlo por regiones, variedades de uva, etc.

Adicionalmente, podríamos pensar en un enfoque alternativo a T-SNE, construyendo un grafo a partir de la matriz de pesos de las similitudes. Esto nos permitiría no solo visualizar los distintos vinos sino también llevar a cabo un análisis de comunidades para detectar grupos de vinos cercanos, lo cual sería un ejercicio interesante para por ejemplo catas a ciegas.

Información de sesión

In [24]:
from sinfo import sinfo
sinfo()
-----
cloudpickle         1.6.0
numpy               1.19.5
pandas              1.2.3
plotly              4.14.3
sinfo               0.3.1
sklearn             0.24.1
tensorflow          2.4.1
tensorflow_hub      0.12.0
tqdm                4.59.0
-----
IPython             7.20.0
jupyter_client      6.1.11
jupyter_core        4.7.1
notebook            6.2.0
-----
Python 3.7.9 (default, Aug 31 2020, 12:42:55) [GCC 7.3.0]
Linux-5.4.0-1045-aws-x86_64-with-debian-buster-sid
2 logical CPU cores, x86_64
-----
Session information updated at 2021-04-28 09:21

Bibliografía


Daniel Cer, Yinfei Yang, Sheng-yi Kong, Nan Hua, Nicole Limtiaco, Rhomni St. John, Noah Constant, Mario Guajardo-Céspedes, Steve Yuan, Chris Tar, Yun-Hsuan Sung, Brian Strope, Ray Kurzweil.Universal Sentence Encoder

Wine reviews dataset

¿Cómo citar este documento?

Sistema de recomendación de vinos con transfer learning y búsqueda semántica by Francisco Espiga, available under a CC BY-NC-SA 4.0 at https://www.cienciadedatos.net/documentos/py33-recomendaciones-busqueda-semantica.html DOI


¿Te ha gustado el artículo? Tu ayuda es importante

Mantener un sitio web tiene unos costes elevados, tu contribución me ayudará a seguir generando contenido divulgativo gratuito. ¡Muchísimas gracias! 😊


Creative Commons Licence
This work by Francisco Espiga is licensed under a Creative Commons Attribution 4.0 International License.