[PYTHON] Visualisons la connaissance de Wikidata avec Neo4j

Ceci est l'article sur le 8ème jour du calendrier de l'Avent 2019, NTT Docomo Service Innovation Department. Cette fois, je vais visualiser les données de ** Wikidata **, qui est l'une des bases de connaissances structurées, en utilisant ** Neo4j **, qui est l'un des DB de graphes.

Qu'est-ce que Graph DB?

En termes simples, il s'agit d'une base de données capable de gérer les structures de graphes. Par rapport au RDB populaire, il s'agit d'une base de données conçue pour gérer les ** relations ** entre les données [^ 1]. À propos, un graphique n'est pas un graphique linéaire ou un graphique à barres, mais une structure de données comme le montre la figure ci-dessous, qui est représentée par un ensemble de nœuds (points de contact) et d'arêtes (branches) (cité de Wikipedia).

6n-graf.png

Je ne sais pas ce qu'il est utile de regarder cette figure seule, mais la structure du graphe est très utile pour exprimer diverses choses dans le monde réel. Par exemple, si vous considérez une gare comme un nœud et une voie ferrée comme un bord, vous pouvez exprimer une carte d'itinéraire sous forme de graphique, et si vous pensez à une ville comme un nœud et une route comme un bord, vous pouvez exprimer un problème de transport. Vous pouvez également représenter graphiquement SNS en regardant les comptes comme des nœuds et les relations entre les comptes comme des arêtes. En termes d'application pratique, il semble que l'historique d'achat puisse être exprimé dans une structure graphique et utilisé pour la recommandation de produit [^ 2].

Monogoto qui peut être exprimé nœud Bord
le plan de route station ligne
logistique ville route
SNS Compte Compte間の関係
Personnel interne Employé Employé同士の関係
Wikipedia 間のリンク

Liens entre les pages Récemment, il semble que la structure du graphe attire davantage l'attention dans le domaine du traitement du langage. Par exemple, à ACL, qui est la plus importante conférence internationale sur le traitement du langage naturel, le nombre d'articles liés au GCN (Graph Convolutional Netowrk) soumis l'année dernière. Était 3 cas, mais cette année, il a considérablement augmenté à 11 cas. Utilisons ce graphe DB pour visualiser la relation entre certains faits. L'extraction d'informations à partir de la visualisation Wikidata nécessite la visualisation des données, mais il est difficile de créer manuellement des données à partir de zéro. Par conséquent, cette fois, nous utiliserons le vidage de ** Wikidata ** [^ 3], qui est une base de connaissances pré-structurée, pour créer les données à importer dans la base de données du graphe. «Structuré» signifie «facile à manipuler avec un ordinateur».

Wikidata est une base de connaissances collaborative et l'un des mêmes projets Wikimedia que Wikipédia. Dans Wikidata, par exemple, la «connaissance» que «la nationalité de John Lennon est le Royaume-Uni» est exprimée en triplés tels que (John Lennon, nationalité, Royaume-Uni). Cette forme de (entité 1, propriété, entité 2) est appelée une ** relation triple **. Vous pouvez considérer l'entité ici comme le titre d'une page sur Wikipedia. Chaque entité a un identifiant spécifique à Wikidata qui commence par «Q» (par exemple, Q5 fait référence à «humain»). De même, la propriété a un identifiant spécifique à Wikidata qui commence par "P".

Toutes les données Wikidata sont vidées au format JSON tous les mercredis, alors utilisons-les comme données à importer dans la base de données graphique. Téléchargez «latest-all.json.bz2» ou «latest-all.json.gz» depuis [ici](https://dumps.wikimedia.org/wikid Catawiki/entities/). Pour plus d'informations sur la structure du JSON à l'intérieur, voir ici.

Pour l'instant, vous pouvez exécuter le script Python comme indiqué ci-dessous pour extraire les informations sur les entités et les propriétés, ou les triplets relationnels, du vidage (notez que cela peut prendre du temps et de la mémoire).

Exemple de script
#!/usr/bin/env python
# coding: utf-8

import bz2
import json
import codecs

triples = []
qs = []
with bz2.BZ2File('latest-all.json.bz2', 'r') as rf, \
     codecs.open('rdf.tsv', 'w', 'utf-8') as rdff, \
     codecs.open('q_id.tsv', 'w', 'utf-8') as qf:
    next(rf)  #Passer la première ligne
    for i, line in enumerate(rf, 1):
        try:
            line = json.loads(line[:-2])
        except json.decoder.JSONDecodeError:
            print(i)
            rdff.write('\n'.join(['\t'.join(x) for x in triples]) + '\n')
            qf.write('\n'.join(['\t'.join(x) for x in qs]) + '\n')
            triples = []
            qs = []
            continue

        try:
            ett_id = line['id']
        except KeyError:
            ett_id = None
        try:
            ett_name = line['labels']['ja']['value']
        except KeyError:
            ett_name = None

        if ett_id is not None and ett_name is not None:
            qs.append((ett_id, ett_name))
            triple = []
            for _, props in line['claims'].items():
                for prop in props:
                    p_id = prop['mainsnak']['property']
                    try:
                        id_ = prop['mainsnak']['datavalue']['value']['id']
                    except Exception as e:
                        # print(ett_id, p_id, e)
                        continue
                    triple.append((ett_id, p_id, id_))
            triples.extend(triple)
            triple = []

        if i % 10000000 == 0:
            print(i)
            rdff.write('\n'.join(['\t'.join(x) for x in triples]) + '\n')
            qf.write('\n'.join(['\t'.join(x) for x in qs]) + '\n')
    rdff.write('\n'.join(['\t'.join(x) for x in triples]) + '\n')
    qf.write('\n'.join(['\t'.join(x) for x in qs]) + '\n')

q_id.tsv est un fichier délimité par des tabulations comme indiqué dans le tableau ci-dessous (ce fichier contient non seulement Q ID mais également P ID).

Q ID Nom de l'entité
Q31 Belgique
Q8 bonheur
Q23 George Washington
Q24 Jack Bauer
Q42 Douglas Adams

De plus, rdf.tsv est des données délimitées par des tabulations, comme indiqué dans le tableau ci-dessous.

Entité 1 Propriété Entité 2
Q31 P1344 Q1088364
Q31 P1151 Q3247091
Q31 P1546 Q1308013
Q31 P5125 Q7112200
Q31 P38 Q4916

Combinez les deux fichiers ci-dessus pour créer deux types de fichiers. Tout d'abord, ajoutez un en-tête à q_id.tsv pour créer et enregistrer les données délimitées par des tabulations nodes.tsv comme indiqué dans le tableau ci-dessous (la troisième colonne: LABEL peut ou non être présente. Comme il n'y a rien de tel, vous pouvez simplement renommer le nom du fichier).

id:ID name :LABEL
Q31 Belgique Entity
Q8 bonheur Entity
Q23 George Washington Entity
Q24 Jack Bauer Entity
Q42 Douglas Adams Entity

Dans le même temps, les propriétés entre les entités sont également enregistrées en tant que données délimitées par des tabulations relations.tsv, comme indiqué dans le tableau ci-dessous. Ajoutez simplement un en-tête à rdf.tsv et remplacez: TYPE dans la deuxième colonne par une chaîne de" P000 "en faisant référence à q_id.tsv (en fait, il n'y a pas besoin de se soucier de le remplacer également. Il n'y a rien de tel, vous pouvez donc simplement renommer le nom du fichier).

:START_ID :TYPE :END_ID
Q23 époux Q191789
Q23 père Q768342
Q23 mère Q458119
Q23 Frères et sœurs Q850421
Q23 Frères et sœurs Q7412891

Il existe différents types de propriétés telles que "nationalité", "conjoint", "lieu de naissance" et "anniversaire", mais il y en a trop, donc dans cette visualisation, entre personnes Je me suis restreint aux propriétés qui peuvent être définies. Par exemple, «parents», «pères», «mères», «maîtres» et «disciples». En outre, les entités sont limitées à celles avec des noms japonais.

Essayez d'utiliser Neo4j

Il existe différents types de DB de graphes, mais ici nous allons introduire Neo4j [^ 4], qui est relativement populaire. Outre Neo4j, je pense qu'Amazon Neptune est célèbre (le nom est cool) [^ 5].

Installation

Pour macOS, il est recommandé d'installer avec Homebrew.

$ brew cask install homebrew/cask-versions/adoptopenjdk8  #Si Java n'est pas inclus
$ brew install neo4j

Si la version de Java est différente

neo4j: Java 1.8 is required to install this formula.
Install AdoptOpenJDK 8 with Homebrew Cask:
  brew cask install homebrew/cask-versions/adoptopenjdk8
Error: An unsatisfied requirement failed this build.

Veuillez noter que vous vous fâcherez.

Si vous entrez $ quel neo4j et que le message / usr / local / bin / neo4j s'affiche, l'installation est terminée.

Importer des données

Importez les nodes.tsv et Relationships.tsv créés précédemment dans Neo4j. Pour importer les données, appuyez sur la commande suivante.

$ neo4j-admin import --nodes ./Downloads/nodes.tsv --relationships ./Downloads/relationships.tsv --delimiter="\t"

Si l'importation réussit

IMPORT DONE in 9s 735ms.
Imported:
  2269484 nodes
  201763 relationships
  6808452 properties
Peak memory usage: 1.05 GB

Est affiché.

Utilisez les commandes suivantes pour démarrer et arrêter le serveur Neo4j.

$ neo4j start  #Au démarrage du serveur
$ neo4j stop  #Lors de l'arrêt du serveur

Après avoir démarré le serveur, essayez d'accéder à http: // localhost: 7474. Lorsque vous accédez pour la première fois, il vous sera demandé de vous connecter, alors entrez "neo4j" dans le nom d'utilisateur et le mot de passe respectivement. Après cela, vous serez invité à changer le mot de passe, alors changez-le en n'importe quel mot de passe.

Traiter les données avec Cypher

Vous êtes maintenant prêt. Utilisons Neo4j immédiatement. Neo4j utilise un langage de type SQL appelé ** Cypher ** comme requête (Cypher est appelé CQL ci-dessous). Veuillez noter que cet article ne fournit pas une explication détaillée de CQL.

Le 8 décembre, lorsque cet article est publié, est la date de décès de John Lennon, alors prenons John Lennon comme sujet. En passant, j'aime le plus "Jealous Guy".

recherche n-hop

Par exemple, si vous souhaitez voir l'entité associée à "John Lennon", émettez le CQL suivant. Ce CQL renvoie tous les nœuds Entity du nœud "John Lennon" vers 3-hop.

match p=((:Entity{name:"John Lennon"})-[*1..3]-()) return p

graph-6.png

C'est un peu déroutant, mais le nœud "John ..." en bas à gauche du centre est le nœud "John Lennon". Par exemple, de «John Lennon» vers le haut, «Ono Yoko» (1) → «Zenzaburo Yasuda» (2) → «Kataoka Jinzaemon» (3) et la relation s'étend à 3-hop. Je vais. La bosse en bas à droite est le familier Paul McCartney et sa famille.

Malheureusement, il n'y a aucun nom de Ringo Star ou George Harrison qui étaient membres des Beatles dans ce graphique.

Recherche d'itinéraire le plus court

Neo4j peut rechercher l'itinéraire le plus court, ce qui est l'une des principales caractéristiques.

Essayons de trouver le chemin le plus court entre "Natsume Soseki" et "Mori Kogai", les maîtres littéraires de l'ère Meiji. Le 8 décembre n'a rien à voir avec ça. En passant, j'aime le plus "Keibi-Kusa".

Émettez un CQL semblable à ce qui suit: Insérez simplement le CQL "Retournez tous les nœuds entre" Natsume Soseki "et" Mori Kogai "" avec le "chemin le plus court".

match p=shortestpath((:Entity{name:"Natsume Soseki"})-[*]-(:Entity{name:"Mori Kogai"})) return p

graph-5.png

"John ..." au sommet n'est pas John Lennon mais "John Manjiro".

Les deux ont vécu en même temps, mais sur la base de la connaissance de Wikidata, la relation est étonnamment distante. D'après les faits historiques, il semble qu'il n'y ait pas eu d'interaction qui semblait être un échange entre eux car ils se connaissaient.

Alors, quel est le chemin le plus court entre "John Lennon" et "Natsume Soseki", qui semblent sans rapport?

match p=shortestpath((:Entity{name:"Natsume Soseki"})-[*]-(:Entity{name:"John Lennon"})) return p

graph-4.png

Après avoir travaillé de "Natsume Soseki" à "Ryunosuke Akutagawa" et "Samuel Beckett" (auteur du drame "Waiting for Godo"), il était célèbre pour "Bob Dylan" ("Like a Rolling Stone" et a remporté le prix Nobel de littérature. ), Après avoir passé le pont entre la littérature et la musique, il semble que vous arriviez à "Jimi Hendrix", "Prince", "Shakira" et "John Lennon". Pour être honnête, je ne peux pas nier le sentiment d'être un peu brusque et détouré [^ 6].

Wikidata est une donnée structurée assez volumineuse, mais elle semble un peu inadéquate pour une utilisation immédiate comme base de connaissances. Vous devrez élargir votre base de connaissances en utilisant, par exemple, ** Extraction de relations **. L'extraction de relation est une technique pour extraire la relation triple (Natsume Soseki, auteur, "Botsuchiyan") de la phrase "Natsume Soseki, l'auteur de" Botsuchiyan "."

Résumé

J'ai essayé de visualiser la connaissance de Wikidata en utilisant Neo4j. Le graphique dessiné avec Neo4j peut être déplacé de manière ondulante sur le navigateur et est amusant, alors veuillez vous entraîner et le toucher vous-même.

[^ 2]: https://ja.wikipedia.org/wiki/ Je connais la théorie des graphes

[^ 5]: M. Hayashi a écrit un article sur notre calendrier de l'Avent l'année dernière → https://qiita.com/dcm_hayashi/items/9b2536b6fbffa0118fad

[^ 6]: Le meilleur ami de "Natsume Soseki" de l'ère du lycée, "Nakamura Yoshiko", est connu comme l'un des confidents de "Goto Shinpei" qui a été président Mantetsu et maire de Tokyo. Après cela, c'est "Zenjiro Yasuda" qui a accepté le plan de ville proposé par Fuji (au fait, la salle publique Hibiya a été construite à cette époque). Et si vous suivez l'arrière-petit-fils de Yasuda comme vous connaissez "Ono Yoko" et son épouse comme "John Lennon", cela peut être beaucoup plus court que le résultat de Wikidata.

Recommended Posts

Visualisons la connaissance de Wikidata avec Neo4j
Visualisez rapidement avec les pandas
Visualisez les réclamations avec l'IA
bac à sable avec neo4j partie 10
Visualisons le fil 2ch avec WordCloud-Scraping-
Visualisez l'arbre de décision avec le notebook Jupyter
Visualisez les dépendances des packages python avec graphviz