El paper Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks presenta una modificación a la arquitectura de BERT para poder crear embeddings que representen semánticamente a cada oración, una técnica similar a la que se usa para crear embeddings que representen caras.
A diferencia de los word embeddings que se representan usando entre 50 y 300 dimensiones, los sentence embeddings que entrega la librería sentence transformers son de dimensión 768.
#!pip install -r requirements.txt
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
from IPython.core.display import HTML
model = SentenceTransformer('bert-base-nli-mean-tokens')
Some weights of the model checkpoint at /home/ec2-user/.cache/torch/sentence_transformers/sbert.net_models_bert-base-nli-mean-tokens/0_BERT were not used when initializing BertModel: ['classifier.weight', 'classifier.bias']
- This IS expected if you are initializing BertModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).
- This IS NOT expected if you are initializing BertModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).
Los word embeddings resultan en representaciones precisas de las palabras, pero no pueden dar cuenta del contexto de las mismas. Como "apple" la fruta y "apple" la marca están representadas por valores idénticos, dependemos de modelos basados en secuencias para poder contextualizarlas y realizar tareas de NLP. Gracias al mecanismo de "atención" que implementa BERT los embeddings de oraciones creados con esta técnica no tienen ese problema. Por eso es interesante ponerlos a prueba con algunos casos específicos.
La forma de medir la distancia entre este tipo de representaciones es la similitud coseno. Por eso vamos a medir la distancia entre una oración y otras dos, tratando de evaluar si efectivamente el modelo coloca más cerca a la que está SEMÁNTICAMENTE más cerca y no a la que más se parece textualmente.
La palabra rock de la primera oración está usada en un sentido distinto que en la segunda oración, y por lo tanto la primera debería parecerse más a la tercera.
sentences = [
"The rolling Stones are rock idols.",
"Don't throw me a rock.",
"Iggy Pop is my favourite artist."
]
sentence_embeddings = model.encode(sentences)
simils = cosine_similarity(
[sentence_embeddings[0],
sentence_embeddings[1]]
)
HTML(f'La similitud entre las frases <b>"{sentences[0]}"</b> <br/> y <b>"{sentences[1]}"</b> , <br/> es de <b>{simils[0][1]}</b>')
La similitud entre las frases "The rolling Stones are rock idols."
y "Don't throw me a rock." ,
es de 0.307941198348999
simils = cosine_similarity(
[sentence_embeddings[0],
sentence_embeddings[2]]
)
HTML(f'La similitud entre las frases <b>"{sentences[0]}"</b> <br/> y <b>"{sentences[2]}"</b> , <br/> es de <b>{simils[0][1]}</b>')
La similitud entre las frases "The rolling Stones are rock idols."
y "Iggy Pop is my favourite artist." ,
es de 0.5321765542030334
En este caso el modelo parece capturar correctamente la diferencia en el significado de rock en el primer y el segundo caso. Si bien la frase sobre Iggy Pop no menciona la palabra rock, habla de música y la similaridad es alta.
¿Qué pasa si describimos un sentimiento equivalente sobre un target equivalente pero expresado con otras palabras?
sentences = [
"I love the capital of Spain.",
"I like Madrid.",
"I love the capital of Portugal.",
"I love the capital of Japan.",
"I hate the capital of Spain.",
"I hate Japan"
]
sentence_embeddings = model.encode(sentences)
outp = ''
for i in range(1,6):
simils = cosine_similarity(
[sentence_embeddings[0],
sentence_embeddings[i]]
)
outp += f'La similitud entre las frases <b>"{sentences[0]}"</b> <br/> y <b>"{sentences[i]}"</b> , <br/> es de <b>{simils[0][1]}</b><br/><br/>'
HTML(outp)
La similitud entre las frases "I love the capital of Spain."
y "I like Madrid." ,
es de 0.8441337943077087
La similitud entre las frases "I love the capital of Spain."
y "I love the capital of Portugal." ,
es de 0.8108397126197815
La similitud entre las frases "I love the capital of Spain."
y "I love the capital of Japan." ,
es de 0.7268274426460266
La similitud entre las frases "I love the capital of Spain."
y "I hate the capital of Spain." ,
es de 0.35674047470092773
La similitud entre las frases "I love the capital of Spain."
y "I hate Japan" ,
es de 0.15219761431217194
Estos resultados muestran lo que esperaríamos: aunque la palabra "love" se reemplaza por "like" y "the capital of Spain" se reemplaza por "Madrid" la frase que realmente expresa lo mismo es "I like Madrid". Las expresiones positivas quedan claramente polarizadas respecto de las negativas. También se refleja el hecho de que Japón es un país semánticamente más alejado de España que Portugal. Esto es algo que se podría ver claramente con word embeddings, pero vemos que esta capacidad tampoco se pierde.
¿Qué pasa si expresamos una misma idea modificando fuertemente el orden de las palabras?
sentences = [
"Don´t shout at me, John.",
"Don´t shout at John.",
"John, stop shouting at me."
]
sentence_embeddings = model.encode(sentences)
outp = ''
for i in range(1,3):
simils = cosine_similarity(
[sentence_embeddings[0],
sentence_embeddings[i]]
)
outp += f'La similitud entre las frases <b>"{sentences[0]}"</b> <br/> y <b>"{sentences[i]}"</b> , <br/> es de <b>{simils[0][1]}</b><br/><br/>'
HTML(outp)
La similitud entre las frases "Don´t shout at me, John."
y "Don´t shout at John." ,
es de 0.8612778186798096
La similitud entre las frases "Don´t shout at me, John."
y "John, stop shouting at me." ,
es de 0.8815945982933044
La primera y la segunda oración son idénticas salvo por una palabra, pero la tercera es la que efectivamente representa el mismo significado y aunque sea con menor confianza los embeddings captan esto correctamente.
Estos embeddings son útiles especialmente para escenarios no supervisados: búsquedas de similitud semántica, clustering de oraciones, etc.