Skip to content

Schéma de base de données PostgreSQL EAV hybride pour l'analyse de textes en français

License

Notifications You must be signed in to change notification settings

thjbdvlt/postgrespacy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

postgrespacy

postgrespacy est un schéma de base de données PostgreSQL pour l'analyse automatique de textes en français, conçu pour stocker les annotations produites par la librairie spaCy. C'est aussi une interface minimale en ligne de commande permettant de facilement ajouter des données, d'annoter les textes (avec spaCy) et de placer le résultat de ces annotations dans la base de données (voir usage, plus bas).

schémas

Les tables du schéma postgrespacy sont destinées à recevoir les données typiquement produites lors de l'annotation automatique par des librairies de NLP (token, word, lemma, pos, dep, feats, etc.). Elles sont organisées de façon à optimiser les performances et l'espace utilisé1.

Un autre schéma optionnel, eav (qui implémente un modèle générique/EAV minimal) peut être ajouté au schéma postgrespacy pour avoir une base de données complète et flexible, mais assez sommaire. Le modèle générique dont il s'inspire, librement emprunté à Francesco Beretta2 (et dont je ne reprends qu'une minuscule partie) est plus complet que ce que désigne le terme EAV (Entity-Attribute-Value), puisqu'il n'implémente pas seulement une manière de décrire les propriétés des entités, mais aussi, par exemple, leurs relations.

Ensemble, ces deux schémas constituent donc un modèle EAV hybride, mais ils sont indépendants.

Le diagramme ci-dessous représente la structure de la base de données. Chaque rectangle représente une table. Les flèches traitillées représentent les héritages entre tables. La table word hérite par exemple de la table token les colonnes sent, i, idx et len3 (voir la documentation de spaCy pour ces propriétés). D'un point de vue conceptuelle, la relation d'héritage correspond à la relation A est une sous-classe de B4. Les autres flèches (pleines) représentent des foreign keys. Les lignes commençant par _ indiquent, elles aussi des foreign keys: la valeur des colonnes en question est toujours integer ou, pour des raisons d'optimisation, smallint, car il est très improbable pour certaines tables de dépasser le millier de lignes (typiquement: les part-of-speech tags et dependency labels, respectivement stockés dans les tables nature et fonction). Les colonnes qui commencent par le signe + représente des valeurs littérales. Si le nom d'une colonne est souligné, cette colonne est utilisée comme primary key (il s'agit toujours de la colonne id).

(Pour une description et détaillées des tables, voir plus bas.)

usage

postgrespacy est aussi une mini-interface en ligne de commande permettant de rapidement ajouter des données dans les tables à partir de fichiers JSON (ou JSONL) ou d'annoter des textes et d'insérer les annotations dans les tables (tokens, lemmes, etc.).

création de la base de données

Pour construire une base de données complète, constituée du schéma postgrespacy et du schéma eav:

psql -c 'create database mydatabase'
postgrespacy schema both -d mydatabase

Pour ajouter le schéma postgrespacy à une base de données existante, il faut spécifier la table qui contient les textes afin que soient générées les foreign keys des tables du schéma. On spécifie cette table via l'option -t, dont l'argument doit avoir la forme <schema.table.primary_key> (la colonne primary key doit être de type integer):

postgrespacy schema postgrespacy -t 'public.texte.id' -d myd

insertion de données (schéma EAV)

Pour importer dans les tables du modèle EAV des données au format JSON ou JSONL (respectant une structure spécifique décrite plus bas):

postgrespacy copy -d 'mydatabase' *.json
postgrespacy copy --dbname 'mydatabase' --jsonl *.jsonl

annotation de textes

Pour annoter des textes avec spaCy et ajouter le résultat des annotations dans les tables, on utilise la commande annotate, qui requière deux arguments: model et query. Ce dernier doit être un fichier (ou - pour lire depuis stdin) contenant une requête SQL retournant deux colonnes: un text (leur contenu) et un int (l'id des textes).

postgrespacy annotate fr_core_news_sm query.sql --dbname mydb
postgrespacy annotate ./path/to/a/model/ - -d mydb  << EOF
SELECT
    val, id
FROM string
EOF

La distinction entre les mot et les autres tokens se fait en utilisant l'attribut Token._.isword. Par défault, tous les tokens seront considérés comme des mots. Si la pipeline utilisée définit une extension Token._.isword, celle-ci sera utilisée pour faire la distinction. L'option -w/--isword permet également de passer une fonction, laquelle doit être enregistrée le registry misc de spaCy.

options

La liste complète des options est disponible via l'option -h, --help.

tables

postgrespacy

Si la structure du schéma postgrespacy n'est pas spécifique à une librairie de NLP5, elle est toutefois désignée de façon à fonctionner avec spaCy. La délimitation des différents objets est peut-être relativement spécifique à la langue française. En particulier, la table lexeme (le mot hors contexte, comme élément du lexique) définit un objet qui regroupe des caractéristiques attribuées par spaCy aux token, mais qui en français ne varient pas d'un contexte à l'autre. En français, peu importe dans quel contexte on rencontrera le mot "magiques", il n'agira toujours de l'adjectif (part-of-speech) "magique" (lemma) au pluriel (morphology), et sa forme graphique canonique (norm) sera toujours "magique". Il est donc inutile d'attribuer ces quatre propriétés à chaque occurrence du mot "magique": les propriétés lemma, pos, norm et morph sont donc, dans une base de données postgrespacy, des propriétés des lexemes tandis que les words ont des propriétés contextuelles: dep (la fonction grammaticale, par exemple "obj"), head (noyau), ainsi que les propriétés héritées des tokens (len, i, idx), à quoi s'ajoute la référence au lexeme dont ils sont une instance. L'ensemble des ligne de la table word constitue donc le discours (les mots réelles) tandis que l'ensemble des lignes de la table lexeme constitue le lexique6 (les mots possibles).

Les mots (table word) eux-mêmes, par ailleurs, sont également un ajout par rapport aux objets utilisés par spaCy qui ne différencie pas les différents types de tokens. Mais, il n'est pas très intéressent d'attribuer des lemmes à des signes de ponctuation, à des urls, à des emoticons ou des chiffres, ni à leur associer une analyse morphologique car les chiffres ne sont pas au pluriel ni les urls fléchies. Ces objets textuels sont donc, dans une base de données postgrespacy, des tokens mais pas des mots, ils n'ont pas de fonction grammaticale (dep) ni de noyau (head), ni non plus de lexème (ce qui est en revanche plus légitime à mon avis). De cette façon, le lexique n'est pas pollué par des nombres, des dates ou des emails (en nombre virtuellement infini).

Lors de l'annotation de textes avec la commande annotate, des vues sont automatiquement générées. Leurs noms sont inspirés par les attributs des Tokens de spaCy: sent_, word_, lexeme_.

  • La vue sent_ contient les attributs de la table sent (le numéro de la phrase dans le texte, i; la position de son premier caractère, idx; l'id du texte dont elle fait partie, string) et ajoute le contenu textuel de la phrase (s), qui ne fait pas partie de la table sent pour éviter de dupliquer des données textuelles (et est, dans la vue, simplement récupéré via la fonction substring()).
  • La vue lexeme_ contient l'id du lexeme et les valeurs des tables pos, dep et morph plutôt que leurs ids. Les features contenues dans la table morph, qui peuvent varier d'un langage à l'autres, sont automatiquement ajoutées en tant que colonnes de la vue lexeme_. Un label morphologique comme Number=Sing|Number[psor]=Plur|Person=3 sera représenté à l'aide des colonnes number, number_psor et person, qui contiendront des valeurs text.
  • La vue word_ contient les propriété de la table word, jointe à la vue lexeme_, et reliées aux tables sent et par.

eav

Le schéma eav est organisé en deux niveaux. Le premier niveau concerne l'ontologie et est constitué des classes d'objets (ex. "personne"), des types de propriétés (ex. "nom") et des types de relations (ex. "connaît"). Le second niveau concerne les individus (lesquels constituent le monde): les objets eux-mêmes (telle personne), les instances de relations (telle relation entre deux personnes particulières) et les instances de propriétés (le nom de telle personne).

  • La table entity regroupe les choses du monde: personnes, lieux, objets matériels, idées, n'importe quoi que l'on veut pouvoir désigner et mettre en relation avec d'autres choses. Ses colonnes sont réduites au minimum: un id qui permet d'y faire référence et une classe qui en définit la nature. La valeur dans la colonne classe est l'identifiant (id) d'une ligne de la table class qui contient aussi les colonnes name (unique et nécessaire) et definition (optionnelle, sans contrainte). La table classe est identique aux tables relation_type et attr_type (qui ont une position et une fonction identique pour les tables relation et attribute), c'est pourquoi elles sont définies dans le diagramme comme étant toutes dérivées d'une table concept (en fait un type et non une table).
  • La table relation met en lien deux entités (sujet et objet, sub et obj).
  • La table attribute permet d'assigner des propriétés aux entités. Une propriété peut optionnellement avoir une valeur et cette valeur peut avoir différents datatypes: le type de propriété "age" requiert une valeur numérique entière (integer), tandis que la propriété "existe" ne nécessite aucune valeur. La propriété "existe" sera donc placée dans la table attribute, qui n'a pas de colonne val tandis que la propriété "age" sera placée dans la table attr_int, laquelle table hérite de la table attribute et possède en plus une colonne val dont la valeur est un entier (integer). Naturellement, il est aussi possible d'insérer manuellement des données "age" comme texte dans la table destinée aux valeurs textuelles, ou dans celle qui est dédiée au format jsonb. Le plus facile, néanmoins, est d'utiliser les modules proposés pour l'importation qui insère automatiquement dans la table appropriée (voir plus bas). (Il y a en réalité davantage de table propriétés que dans le diagramme ci-dessous.)

C'est par la table string que sont mises en lien les deux parties de la base de données. Elle hérite de la table attribute, tout comme les tables attr_int ou attr_float mais elle a également une colonne id qui est référencée par les table par, sent, span.

format d'importation (eav)

Si l'insertion d'entités, de propriétés ou de relations peut évidemment se faire manuellement, il est aussi possible d'importer des données structurées au format JSON comme suit, chaque objet JSON décrivant une entité, ses propriétés et les relations dont elle est le sujet.

[
    {"id": 1, "classe": "bibliothèque"},
    {"id": 2, "classe": "lieu", "est_magique": null, "magicité": 1.2},
    {
        "classe": "personne", "nom": "becky", "relations": [
            {"type": "fréquente", "objet": 1}
        ]
    },
    {"classe": "livre", "relations": [{"type": "dans", "objet": 1}]},
]

Le seul champ requis est, pour chaque entité, le champ class. Le champ id permet de définir les relations entre les entités (il ne correspond pas à l'id de l'entité dans la base de données). Dans les entités, tous les champs qui ne sont pas class, id ou relations sont interprétés comme des propriétés et sont insérées dans les tables qui correspondent au datatype:

{"est_magique": None}  # ira dans la table `attribute`, sans valeur (`null` en JSON!)
{"nom": "becky"}       # ira dans la table `string`
{"date_birth": 1231}          # ira dans la table `attr_int`
{"y": 1.2}           # ira dans la table `attr_float`
{"noms": {"prénom": "!", "nom": "?"}}  # ira dans la table `attr_jsonb`
{"?": [1, 2, 3]}  # idem

Une exception concerne la table d'attribut xml. Y seront ajouté les propriétés dont le nom est préfixé par xml_, par exemple xml_titre ou xml_contenu. Des fonctions permettent de retirer les balises pour l'analyse avec spaCy et de les remettre plus tard^[La documentation pour ces fonctions est en cours d'écriture.].

L'importation se fait à l'aide de la commande copy. Tous les arguments positionnels sont traités comme des fichiers à importer:

postgrespacy copy -d mydatabase data1.json data2.json ../*.json

concordance

Des fonctions minimales sont disponibles pour des concordances. Une utilisation minimale:

select (concord (w, 50)).*
from word_ w where w.lemma in ('écrire', 'calculer')
order by right_context;

Pour que l'ordre de tri se fasse sur le contexte gauche, il faut passer par une CTE:

WITH x AS (
    SELECT
        (concord (w, 50)).*
    FROM
        word_ w
    /* écrire ici les conditions */
    WHERE
        w.norm = 'écrire'
)
SELECT
    x.left_context,
    x.pivot,
    x.right_context
FROM
    x
ORDER BY
    reverse(x.left_context),
    x.right_context;

TODO

  • Fonction d'ajout automatique de span à partir de html/xml.
  • Ajout des annotations de ents et spans.
  • Ajout automatique des custom attributes des tokens.
  • Ajout (EAV) depuis HTML, MarkDown, ou plain text.
  • Token.tag (XPOS).
  • Génération dynamique des vues pour le schéma EAV.

Footnotes

  1. Voir les principes généraux de la normalisation de base de données: https://en.wikipedia.org/wiki/Database_normalization

  2. Francesco Beretta, Des sources aux données structurées, 14 octobre 2022, CC BY-SA 4.0. En ligne

  3. Les foreign keys ne se transmettent pas par héritage; elles sont systématiquement ajoutées dans la définition du schéma, ainsi que toutes les autres contraintes.

  4. Exemple typique, extrait de la documentation de PostgreSQL: villes et capitales; la table capitale hérite de la table ville (les capitales sont un type spécifique de ville), auquel est ajoutée des propriétés ou contraintes (ex. "état").

  5. Natural Language Processing (analyse automatique de textes en langage naturel).

  6. De la même manière que dans n'importe quel lexique ou dictionnaire, un même forme graphique peut être utilisée dans différentes entrées lexicale: être-verbe, être-nom, etc.

About

Schéma de base de données PostgreSQL EAV hybride pour l'analyse de textes en français

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published