Knowledge Graph Il Cambiamento di Gioco in AI e Data Science

Knowledge Graph Il Cambiamento di Gioco in AI e Data Science

Introduzione

I knowledge graph sono emersi come un approccio potente e versatile nell’ambito dell’IA e della Data Science per registrare informazioni strutturate al fine di favorire il recupero dei dati, il ragionamento e l’inferenza. Questo articolo esamina i knowledge graph di ultima generazione, inclusa la costruzione, la rappresentazione, l’interrogazione, gli embeddings, il ragionamento, l’allineamento e la fusione.

Discutiamo anche le molteplici applicazioni dei knowledge graph, come i motori di raccomandazione e i sistemi di domande e risposte. Infine, al fine di aprire la strada a nuovi progressi e opportunità di ricerca, esploriamo i problemi e le future possibili direzioni di questo argomento.

I knowledge graph hanno rivoluzionato il modo in cui le informazioni sono organizzate e utilizzate, fornendo un meccanismo flessibile e scalabile per esprimere connessioni complesse tra entità e caratteristiche. Qui forniamo una introduzione generale ai knowledge graph, alla loro importanza e al loro potenziale utilizzo in vari campi.

Obiettivo di apprendimento

  • Comprendere il concetto e lo scopo dei knowledge graph come rappresentazioni strutturate delle informazioni.
  • Apprendere i componenti chiave dei knowledge graph: nodi, archi e proprietà.
  • Esplorare il processo di costruzione, inclusi le tecniche di estrazione e integrazione dei dati.
  • Comprendere come gli embeddings dei knowledge graph rappresentano entità e relazioni come vettori continui.
  • Esplorare metodi di ragionamento per dedurre nuove intuizioni dalle conoscenze esistenti.
  • Acquisire conoscenze sulla visualizzazione dei knowledge graph per una migliore comprensione.

Questo articolo è stato pubblicato come parte del Data Science Blogathon.

Cos’è un Knowledge Graph?

Un knowledge graph può memorizzare le informazioni estratte durante un’operazione di estrazione delle informazioni. Molte implementazioni fondamentali di knowledge graph utilizzano l’idea di una tripla, che è una collezione di tre elementi (un soggetto, un predicato e un oggetto) che possono contenere informazioni su qualsiasi cosa.

Un grafo è una collezione di nodi e archi.

Questo è il più piccolo knowledge graph che possiamo progettare, noto anche come tripla. I knowledge graph possono assumere diverse forme e dimensioni. Qui, il Nodo A e il Nodo B sono due cose separate. Questi nodi sono collegati da un arco che mostra la relazione tra i due nodi.

Rappresentazione dei Dati nel Knowledge Graph

Prendiamo la seguente frase come esempio:

Londra è la capitale dell’Inghilterra. Westminster si trova a Londra.

Vedremo alcune elaborazioni di base in seguito, ma inizialmente avremmo due triple che appaiono così:

(Londra, essere capitale, Inghilterra), (Westminster, localizzare, Londra)

In questo esempio, abbiamo tre entità distinte (Londra, Inghilterra e Westminster) e due relazioni (capitale, localizzazione). La costruzione di un knowledge graph richiede solo due nodi correlati nella rete con le entità e i vertici con le relazioni. La struttura risultante è la seguente: La creazione manuale di un knowledge graph non è scalabile. Nessuno esaminerà centinaia di pagine per estrarre tutte le entità e le loro relazioni!

Poiché possono esaminare facilmente centinaia o addirittura migliaia di documenti, i robot sono più adatti a gestire questo lavoro rispetto alle persone. Il fatto che le macchine non possano comprendere il linguaggio naturale presenta un’altra difficoltà. In questo caso, è importante utilizzare l’elaborazione del linguaggio naturale (NLP).

Rendere il nostro computer in grado di comprendere il linguaggio naturale è cruciale se vogliamo creare un knowledge graph dal testo. Utilizzando metodi NLP per fare ciò, tra cui la segmentazione delle frasi, l’analisi delle dipendenze, il tagging delle parti del discorso e il riconoscimento delle entità.

Importa Dipendenze e Carica il dataset

import re
import pandas as pd
import bs4
import requests
import spacy
from spacy import displacy
nlp = spacy.load('en_core_web_sm')

from spacy.matcher import Matcher 
from spacy.tokens import Span 

import networkx as nx

import matplotlib.pyplot as plt
from tqdm import tqdm

pd.set_option('display.max_colwidth', 200)
%matplotlib inline

# importa le frasi di Wikipedia
candidate_sentences = pd.read_csv("../input/wiki-sentences1/wiki_sentences_v2.csv")
candidate_sentences.shape

candidate_sentences['sentence'].sample(5)

Segmentazione delle Frasi

La divisione del testo dell’articolo o del documento in frasi è la prima fase nella creazione di un knowledge graph. Successivamente, selezioneremo solo le frasi che hanno esattamente un soggetto e un oggetto.

doc = nlp("the drawdown process is governed by astm standard d823")

for tok in doc:
  print(tok.text, "...", tok.dep_)

Estrazione delle Entità

Un componente di una frase composto da una sola parola può essere facilmente rimosso. Possiamo ottenere ciò rapidamente utilizzando i tag delle parti del discorso (POS). I sostantivi e i nomi propri sarebbero le nostre entità.

Quando un’entità si estende su molte parole, i tag POS da soli non sono sufficienti. È necessario analizzare l’albero delle dipendenze dell’affermazione.

I nodi e le loro relazioni sono molto importanti nello sviluppo di un grafo di conoscenza.

Questi nodi saranno composti da entità trovate nei testi di Wikipedia. I collegamenti rappresentano le relazioni tra questi elementi. Utilizzeremo un approccio non supervisionato per estrarre questi elementi dalla struttura della frase.

L’idea di base è leggere una frase e identificare il soggetto e l’oggetto man mano che li si incontra. Tuttavia, ci sono alcuni svantaggi. Ad esempio, “vino rosso” è un’entità che si estende su più parole, mentre i parser di dipendenza identificano solo parole singole come soggetti o oggetti.

A causa dei problemi sopra menzionati, ho creato il codice qui sotto per estrarre il soggetto e l’oggetto (entità) da una frase. Per comodità, ho suddiviso il codice in molte sezioni:

def get_entities(sent):
  ## chunk 1
  ent1 = ""
  ent2 = ""

  prv_tok_dep = ""    # etichetta di dipendenza del token precedente nella frase
  prv_tok_text = ""   # token precedente nella frase

  prefix = ""
  modifier = ""

  #############################################################
  
  for tok in nlp(sent):
    ## chunk 2
    # se il token è un segno di punteggiatura, passa al token successivo
    if tok.dep_ != "punct":
      # controlla: il token è una parola composta o no
      if tok.dep_ == "compound":
        prefix = tok.text
        # se la parola precedente era anche una 'compound', aggiungi la parola corrente ad essa
        if prv_tok_dep == "compound":
          prefix = prv_tok_text + " "+ tok.text
      
      # controlla: il token è un modificatore o no
      if tok.dep_.endswith("mod") == True:
        modifier = tok.text
        # se la parola precedente era anche una 'compound', aggiungi la parola corrente ad essa
        if prv_tok_dep == "compound":
          modifier = prv_tok_text + " "+ tok.text
      
      ## chunk 3
      if tok.dep_.find("subj") == True:
        ent1 = modifier +" "+ prefix + " "+ tok.text
        prefix = ""
        modifier = ""
        prv_tok_dep = ""
        prv_tok_text = ""      

      ## chunk 4
      if tok.dep_.find("obj") == True:
        ent2 = modifier +" "+ prefix +" "+ tok.text
        
      ## chunk 5  
      # aggiorna le variabili
      prv_tok_dep = tok.dep_
      prv_tok_text = tok.text
  #############################################################

  return [ent1.strip(), ent2.strip()]

Sezione 1

Questo blocco di codice definisce diverse variabili vuote. La dipendenza della parola precedente e la parola stessa verranno mantenute nelle variabili prv_tok_dep e prv_tok_text, rispettivamente. Il prefisso e il modificatore conterranno il testo associato al soggetto o all’oggetto.

Sezione 2

Successivamente, analizzeremo tutti i token della frase uno per uno. Sarà stabilito prima lo status del token come segno di punteggiatura. In tal caso, lo ignoreremo e passeremo al token successivo. Se il token è una componente di una frase composta (tag di dipendenza = “compound”), lo inseriremo nella variabile prefisso.

Le persone combinano molte parole insieme per formare una parola composta e generare una nuova frase con un nuovo significato (esempi includono “stadio di calcio” e “amante degli animali”).

Aggiungeranno questo prefisso a ciascun soggetto o oggetto nella frase. Un metodo simile verrà utilizzato per gli aggettivi come “bella camicia”, “grande casa”, e così via.

Sezione 3

Se il soggetto è il token in questo scenario, verrà inserito come prima entità nella variabile ent1. Le variabili prefisso, modificatore, prv_tok_dep e prv_tok_text saranno tutte reimpostate.

Sezione 4

Se il token è l’oggetto, verrà inserito come seconda entità nella variabile ent2. Le variabili prefisso, modificatore, prv_tok_dep e prv_tok_text saranno tutte reimpostate.

Sezione 5

Dopo aver determinato il soggetto e l’oggetto della frase, aggiorneremo il token precedente e il suo tag di dipendenza.

Utilizziamo una frase per testare questa funzione:

get_entities("il film aveva 200 brevetti")

Wow, tutto sembra procedere come previsto. Nella frase sopra, ‘film’ è l’argomento e ‘200 brevetti’ è l’obiettivo.

Ora possiamo utilizzare questo approccio per estrarre queste coppie di entità per tutte le frasi nei nostri dati:

entity_pairs = []

for i in tqdm(candidate_sentences["sentence"]):
  entity_pairs.append(get_entities(i))

L’elenco entity_pairs include tutte le coppie soggetto-oggetto delle frasi di Wikipedia. Diamo un’occhiata a alcune di esse.

entity_pairs[10:20]

Come puoi vedere, alcune coppie di entità contengono pronomi come ‘we’, ‘it’, ‘she’, e così via. Invece, vorremmo nomi propri o sostantivi. Potremmo eventualmente aggiornare il codice get_entities() per filtrare i pronomi.

Estrazione delle relazioni

L’estrazione delle entità è solo metà del compito. Abbiamo bisogno di archi per collegare i nodi (entità) e formare un grafo di conoscenza. Questi archi rappresentano le connessioni tra due nodi.

Secondo la nostra ipotesi, il predicato è il verbo principale di una frase. Ad esempio, nella frase “Sessanta musical di Hollywood sono stati rilasciati nel 1929”, il verbo “rilasciato nel” viene utilizzato come predicato per il triplo formato da questa frase.

La seguente funzione può estrarre tali predicati dalle frasi. Ho utilizzato il matching basato su regole di spaCy in questo caso:

def get_relation(sent):

  doc = nlp(sent)

  # Oggetto della classe Matcher
  matcher = Matcher(nlp.vocab)

  # Definisci il pattern
  pattern = [{'DEP':'ROOT'}, 
            {'DEP':'prep','OP':"?"},
            {'DEP':'agent','OP':"?"},  
            {'POS':'ADJ','OP':"?"}] 

  matcher.add("matching_1", None, pattern) 

  matches = matcher(doc)
  k = len(matches) - 1

  span = doc[matches[k][1]:matches[k][2]] 

  return(span.text)

Il pattern della funzione cerca di individuare la parola RADICE o il verbo principale della frase. Dopo aver identificato la RADICE, il pattern controlla se è seguita da una preposizione (‘prep’) o da una parola agente. In tal caso, viene aggiunta alla parola RADICE. Permettimi di mostrarti questa funzione:

get_relation("John ha completato il compito")

relations = [get_relation(i) for i in tqdm(candidate_sentences['sentence'])]

Diamo un’occhiata alle relazioni o predicati più comuni che abbiamo appena estratto:

pd.Series(relations).value_counts()[:50]

Costruire un grafo di conoscenza

Infine, costruiremo un grafo di conoscenza utilizzando le entità recuperate (coppie soggetto-oggetto) e i predicati (relazioni tra le entità). Costruiamo un dataframe con entità e predicati:

# estrarre il soggetto
source = [i[0] for i in entity_pairs]

# estrarre l'oggetto
target = [i[1] for i in entity_pairs]

kg_df = pd.DataFrame({'source':source, 'target':target, 'edge':relations})

Successivamente, utilizzeremo la libreria networkx per creare una rete da questo dataframe. I nodi rappresenteranno le entità, mentre gli archi o le connessioni tra i nodi rifletteranno le relazioni tra i nodi.

Sarà un grafo diretto. In altre parole, la relazione tra due nodi collegati è unidirezionale, da un nodo all’altro.

# creare un grafo diretto da un dataframe
G=nx.from_pandas_edgelist(kg_df, "source", "target", 
                          edge_attr=True, create_using=nx.MultiDiGraph())

plt.figure(figsize=(12,12))

pos = nx.spring_layout(G)
nx.draw(G, with_labels=True, node_color='skyblue', edge_cmap=plt.cm.Blues, pos = pos)
plt.show()

Proviamo a tracciare la rete con un piccolo esempio:

import networkx as nx
import matplotlib.pyplot as plt

# Crea una classe KnowledgeGraph
class KnowledgeGraph:
    def __init__(self):
        self.graph = nx.DiGraph()

    def add_entity(self, entity, attributes):
        self.graph.add_node(entity, **attributes)

    def add_relation(self, entity1, relation, entity2):
        self.graph.add_edge(entity1, entity2, label=relation)

    def get_attributes(self, entity):
        return self.graph.nodes[entity]

    def get_related_entities(self, entity, relation):
        related_entities = []
        for _, destination, rel_data in self.graph.out_edges(entity, data=True):
            if rel_data["label"] == relation:
                related_entities.append(destination)
        return related_entities


if __name__ == "__main__":
    # Inizializza il knowledge graph
    knowledge_graph = KnowledgeGraph()

    # Aggiungi entità e i loro attributi
    knowledge_graph.add_entity("Stati Uniti", {"Capitale": "Washington, D.C.", "Continente": "America del Nord"})
    knowledge_graph.add_entity("Francia", {"Capitale": "Parigi", "Continente": "Europa"})
    knowledge_graph.add_entity("Cina", {"Capitale": "Pechino", "Continente": "Asia"})

    # Aggiungi relazioni tra le entità
    knowledge_graph.add_relation("Stati Uniti", "Vicino a", "Canada")
    knowledge_graph.add_relation("Stati Uniti", "Vicino a", "Messico")
    knowledge_graph.add_relation("Francia", "Vicino a", "Spagna")
    knowledge_graph.add_relation("Francia", "Vicino a", "Italia")
    knowledge_graph.add_relation("Cina", "Vicino a", "India")
    knowledge_graph.add_relation("Cina", "Vicino a", "Russia")

    # Recupera e stampa gli attributi e le entità correlate
    print("Attributi della Francia:", knowledge_graph.get_attributes("Francia"))
    print("Vicini della Cina:", knowledge_graph.get_related_entities("Cina", "Vicino a"))

    # Visualizza il knowledge graph
    pos = nx.spring_layout(knowledge_graph.graph, seed=42)
    edge_labels = nx.get_edge_attributes(knowledge_graph.graph, "label")

    plt.figure(figsize=(8, 6))
    nx.draw(knowledge_graph.graph, pos, with_labels=True, 
                      node_size=2000, node_color="skyblue", font_size=10)
    nx.draw_networkx_edge_labels(knowledge_graph.graph, pos, 
                                edge_labels=edge_labels, font_size=8)
    plt.title("Knowledge Graph: Paesi e le loro Capitali")
    plt.show()

Questo non è esattamente quello che stavamo cercando (ma è ancora piuttosto interessante!). Abbiamo scoperto di aver generato un grafo con tutte le relazioni che avevamo. Un grafo con così tante relazioni o predicati diventa piuttosto difficile da visualizzare.

Di conseguenza, è meglio utilizzare solo alcune relazioni chiave per visualizzare un grafo. Affronterò una relazione alla volta. Iniziamo con la relazione “composto da”:

G=nx.from_pandas_edgelist(kg_df[kg_df['edge']=="composto da"], 
                            "source", "target", 
                          edge_attr=True, create_using=nx.MultiDiGraph())

plt.figure(figsize=(12,12))
pos = nx.spring_layout(G, k = 0.5) 
nx.draw(G, with_labels=True, node_color='skyblue', 
                                node_size=1500, edge_cmap=plt.cm.Blues, pos = pos)
plt.show()

Questo è un grafo molto migliore. Le frecce in questo caso indicano i compositori. Nel grafo sopra, A.R. Rahman, un famoso compositore musicale, è collegato a cose come “colonna sonora”, “colonna sonora del film” e “musica”.

Guardiamo altre connessioni. Adesso voglio disegnare il grafo per la relazione “scritto da”:

G=nx.from_pandas_edgelist(kg_df[kg_df['edge']=="scritto da"], "source", 
                            "target", 
                          edge_attr=True, create_using=nx.MultiDiGraph())

plt.figure(figsize=(12,12))
pos = nx.spring_layout(G, k = 0.5)
nx.draw(G, with_labels=True, node_color='skyblue', node_size=1500, 
    edge_cmap=plt.cm.Blues, pos = pos)
plt.show()

Questo knowledge graph ci fornisce dati sorprendenti. Tra i famosi parolieri ci sono Javed Akhtar, Krishna Chaitanya e Jaideep Sahni; questo grafo rappresenta eloquentemente la loro relazione.

Guardiamo il knowledge graph per un’altra relazione cruciale, “rilasciato in”:

G = nx.from_pandas_edgelist(kg_df[kg_df['edge'] == "rilasciato in"],
                           "source", "target", 
                          edge_attr=True, create_using=nx.MultiDiGraph())

plt.figure(figsize=(12,12))
pos = nx.spring_layout(G, k=0.5)
nx.draw(G, with_labels=True, node_color='skyblue', node_size=1500,
                       edge_cmap=plt.cm.Blues, pos=pos)
plt.show()

Conclusion

In conclusione, i grafi di conoscenza sono emersi come uno strumento potente e versatile nell’ambito dell’Intelligenza Artificiale e della scienza dei dati per rappresentare informazioni strutturate, consentendo un efficiente recupero, ragionamento e inferenza dei dati. In questo articolo, abbiamo esplorato i punti chiave che evidenziano l’importanza e l’impatto dei grafi di conoscenza in diversi settori. Ecco i punti chiave:

  • I grafi di conoscenza offrono una rappresentazione strutturata delle informazioni in un formato a grafo con nodi, archi e proprietà.
  • Consentono la modellazione flessibile dei dati senza schemi fissi, facilitando l’integrazione dei dati da fonti diverse.
  • Il ragionamento sui grafi di conoscenza consente di inferire nuovi fatti e intuizioni sulla base delle conoscenze esistenti.
  • Le applicazioni spaziano in diversi settori, tra cui l’elaborazione del linguaggio naturale, i sistemi di raccomandazione e i motori di ricerca semantici.
  • Le rappresentazioni dei grafi di conoscenza rappresentano entità e relazioni in vettori continui, consentendo il machine learning sui grafi.

In conclusione, i grafi di conoscenza sono diventati essenziali per organizzare e dare senso a grandi quantità di informazioni interconnesse. Con l’avanzare della ricerca e della tecnologia, i grafi di conoscenza svolgeranno senza dubbio un ruolo centrale nel plasmare il futuro dell’Intelligenza Artificiale, della scienza dei dati, del recupero delle informazioni e dei sistemi decisionali in vari settori.

Domande frequenti

I media mostrati in questo articolo non sono di proprietà di Analytics Vidhya e sono utilizzati a discrezione dell’autore.