Gráfico para conectar frases
Tenho uma lista de frases de alguns tópicos (dois) como a seguir:
Sentences
Trump says that it is useful to win the next presidential election.
The Prime Minister suggests the name of the winner of the next presidential election.
In yesterday's conference, the Prime Minister said that it is very important to win the next presidential election.
The Chinese Minister is in London to discuss about climate change.
The president Donald Trump states that he wants to win the presidential election. This will require a strong media engagement.
The president Donald Trump states that he wants to win the presidential election. The UK has proposed collaboration.
The president Donald Trump states that he wants to win the presidential election. He has the support of his electors.
Como você pode ver, há semelhanças nas frases.
Estou tentando relacionar várias frases e visualizar as características delas usando um gráfico (direcionado). O gráfico é construído a partir de uma matriz de similaridade, aplicando a ordem das linhas das sentenças conforme mostrado acima. Criei uma nova coluna, Tempo, para mostrar a ordem das sentenças, então a primeira linha (Trump diz que ....) está no tempo 1; a segunda linha (o primeiro-ministro sugere ...) é no momento 2 e assim por diante. Algo assim
Time Sentences
1 Trump said that it is useful to win the next presidential election.
2 The Prime Minister suggests the name of the winner of the next presidential election.
3 In today's conference, the Prime Minister said that it is very important to win the next presidential election.
...
Gostaria, então, de encontrar as relações para ter uma visão geral clara do tema. Vários caminhos para uma frase mostram que há várias informações associadas a ela. Para determinar a semelhança entre duas frases, tentei extrair substantivos e verbos da seguinte forma:
noun=[]
verb=[]
for index, row in df.iterrows():
nouns.append([word for word,pos in pos_tag(row[0]) if pos == 'NN'])
verb.append([word for word,pos in pos_tag(row[0]) if pos == 'VB'])
pois são palavras-chave em qualquer frase. Portanto, quando uma palavra-chave (substantivo ou verbo) aparece na frase x, mas não nas outras frases, ela representa uma diferença entre essas duas frases. Acho que uma abordagem melhor, no entanto, poderia ser usando word2vec ou gensim (WMD).
Essa similaridade deve ser calculada para cada frase. Eu gostaria de construir um gráfico que mostre o conteúdo da frase no meu exemplo acima. Como há dois tópicos (Trump e Ministro chinês), para cada um deles preciso procurar subtópicos. Trump tem subtópico eleição presidencial, por exemplo. Um nó em meu gráfico deve representar uma frase. Palavras em cada nó representam diferenças para as frases, mostrando novas informações na frase. Por exemplo, a palavra statesna frase no tempo 5 está nas frases adjacentes nos tempos 6 e 7. Eu gostaria apenas de encontrar uma maneira de obter resultados semelhantes aos mostrados na imagem abaixo. Tentei usar principalmente a extração de substantivos e verbos, mas provavelmente não é a maneira certa de proceder. O que tentei fazer foi considerar a frase no tempo 1 e compará-la com outras frases, atribuindo uma pontuação de similaridade (com extração de substantivos e verbos, mas também com palavra2vec), e repeti-la para todas as outras frases. Mas meu problema agora é como extrair a diferença para criar um gráfico que faça sentido.
Para a parte do gráfico, eu consideraria usar networkx (DiGraph):
G = nx.DiGraph()
N = Network(directed=True)
para mostrar a direção dos relacionamentos.
Eu forneci um exemplo diferente para ficar mais claro (mas se você trabalhou com o exemplo anterior, também estaria bem. Peço desculpas pelo transtorno, mas como minha primeira pergunta não foi tão clara, tive que fornecer também uma melhor, provavelmente mais fácil, exemplo).
Respostas
Não implementou PNL para separação verbo / substantivo, apenas adicionou uma lista de palavras boas. Eles podem ser extraídos e normalizou com spacy relativamente fácil. Observe que walkocorre em 1,2,5 sentenças e forma uma tríade.
import re
import networkx as nx
import matplotlib.pyplot as plt
plt.style.use("ggplot")
sentences = [
"I went out for a walk or walking.",
"When I was walking, I saw a cat. ",
"The cat was injured. ",
"My mum's name is Marylin.",
"While I was walking, I met John. ",
"Nothing has happened.",
]
G = nx.Graph()
# set of possible good words
good_words = {"went", "walk", "cat", "walking"}
# remove punctuation and keep only good words inside sentences
words = list(
map(
lambda x: set(re.sub(r"[^\w\s]", "", x).lower().split()).intersection(
good_words
),
sentences,
)
)
# convert sentences to dict for furtehr labeling
sentences = {k: v for k, v in enumerate(sentences)}
# add nodes
for i, sentence in sentences.items():
G.add_node(i)
# add edges if two nodes have the same word inside
for i in range(len(words)):
for j in range(i + 1, len(words)):
for edge_label in words[i].intersection(words[j]):
G.add_edge(i, j, r=edge_label)
# compute layout coords
coord = nx.spring_layout(G)
plt.figure(figsize=(20, 14))
# set label coords a bit upper the nodes
node_label_coords = {}
for node, coords in coord.items():
node_label_coords[node] = (coords[0], coords[1] + 0.04)
# draw the network
nodes = nx.draw_networkx_nodes(G, pos=coord)
edges = nx.draw_networkx_edges(G, pos=coord)
edge_labels = nx.draw_networkx_edge_labels(G, pos=coord)
node_labels = nx.draw_networkx_labels(G, pos=node_label_coords, labels=sentences)
plt.title("Sentences network")
plt.axis("off")
Atualizar
Se você deseja medir a similaridade entre diferentes sentenças, você pode desejar calcular a diferença entre a incorporação de sentenças.
Isso lhe dá a oportunidade de encontrar semelhanças semânticas entre frases com palavras diferentes, como "Um jogo de futebol com vários homens jogando" e "Alguns homens estão jogando um esporte". Quase uma abordagem SoTA usando BERT pode ser encontrada aqui , abordagens mais simples estão aqui .
Já que você tem medida de similaridade, apenas substitua o bloco add_edge para adicionar uma nova aresta somente se a medida de similaridade for maior que algum limite. O código de adição de bordas resultante será semelhante a este:
# add edges if two nodes have the same word inside
tresold = 0.90
for i in range(len(words)):
for j in range(i + 1, len(words)):
# suppose you have some similarity function using BERT or PCA
similarity = check_similarity(sentences[i], sentences[j])
if similarity > tresold:
G.add_edge(i, j, r=similarity)
Uma maneira de lidar com isso é tokenizar, remover as palavras de parada e criar o vocabulário. Em seguida, desenhe o gráfico com base neste vocabulário. Estou mostrando abaixo um exemplo de tokens baseados em unigrama, mas uma abordagem muito melhor será identificar frases (ngrams) e usá-las como vocabulário em vez de unigramas. A frase da mesma forma será representada pictoricamente por nós (e as frases correspondentes) tendo mais em e grau.
Amostra:
from sklearn.feature_extraction.text import CountVectorizer
import networkx as nx
import matplotlib.pyplot as plt
corpus = [
"Trump says that it is useful to win the next presidential election",
"The Prime Minister suggests the name of the winner of the next presidential election",
"In yesterday conference, the Prime Minister said that it is very important to win the next presidential election",
"The Chinese Minister is in London to discuss about climate change",
"The president Donald Trump states that he wants to win the presidential election. This will require a strong media engagement",
"The president Donald Trump states that he wants to win the presidential election. The UK has proposed collaboration",
"The president Donald Trump states that he wants to win the presidential election. He has the support of his electors",
]
vectorizer = CountVectorizer(analyzer='word', ngram_range=(1, 1), stop_words="english")
vectorizer.fit_transform(corpus)
G = nx.DiGraph()
G.add_nodes_from(vectorizer.get_feature_names())
all_edges = []
for s in corpus:
edges = []
previous = None
for w in s.split():
w = w.lower()
if w in vectorizer.get_feature_names():
if previous:
edges.append((previous, w))
#print (previous, w)
previous = w
all_edges.append(edges)
plt.figure(figsize=(20,20))
pos = nx.shell_layout(G)
nx.draw_networkx_nodes(G, pos, node_size = 500)
nx.draw_networkx_labels(G, pos)
colors = ['r', 'g', 'b', 'y', 'm', 'c', 'k']
for i, edges in enumerate(all_edges):
nx.draw_networkx_edges(G, pos, edgelist=edges, edge_color=colors[i], arrows=True)
#nx.draw_networkx_edges(G, pos, edgelist=black_edges, arrows=False)
plt.show()
Resultado: