web-dev-qa-db-fra.com

Comment regrouper les catégories wikipedia en python?

Pour chaque concept de mon ensemble de données, j'ai stocké les catégories wikipedia correspondantes. Par exemple, considérons les 5 concepts suivants et leurs catégories wikipedia correspondantes.

  • hypertriglycéridémie: ['Category:Lipid metabolism disorders', 'Category:Medical conditions related to obesity']
  • inhibiteur d'enzyme: ['Category:Enzyme inhibitors', 'Category:Medicinal chemistry', 'Category:Metabolism']
  • chirurgie de pontage: ['Category:Surgery stubs', 'Category:Surgical procedures and techniques']
  • perth: ['Category:1829 establishments in Australia', 'Category:Australian capital cities', 'Category:Metropolitan areas of Australia', 'Category:Perth, Western Australia', 'Category:Populated places established in 1829']
  • climat: ['Category:Climate', 'Category:Climatology', 'Category:Meteorological concepts']

Comme vous pouvez le voir, les trois premiers concepts appartiennent au domaine médical (alors que les deux termes restants ne sont pas des termes médicaux).

Plus précisément, je veux diviser mes concepts en médical et non médical. Cependant, il est très difficile de diviser les concepts en utilisant uniquement les catégories. Par exemple, même si les deux concepts enzyme inhibitor et bypass surgery sont dans le domaine médical, leurs catégories sont très différentes les unes des autres.

Par conséquent, je voudrais savoir s'il existe un moyen d'obtenir le parent category des catégories (par exemple, les catégories de enzyme inhibitor et bypass surgery appartient à medical catégorie parent)

J'utilise actuellement pymediawiki et pywikibot. Cependant, je ne suis pas limité à ces deux bibliothèques et je suis heureux d'avoir également des solutions utilisant d'autres bibliothèques.

[~ # ~] modifier [~ # ~]

Comme suggéré par @IlmariKaronen, j'utilise également le categories of categories et les résultats que j'ai obtenus sont les suivants (T la petite police près du category est le categories of the category). enter image description here

Cependant, je ne pouvais toujours pas trouver un moyen d'utiliser ces détails de catégorie pour décider si un terme donné était médical ou non médical.

De plus, comme l'a souligné @IlmariKaronen, l'utilisation de Wikiproject les détails peuvent être potentiels. Cependant, il semble que le wiki_pro Medicine ne semble pas avoir tous les termes médicaux. Par conséquent, nous devons également vérifier d'autres projets wiki.

EDIT: Mon code actuel d'extraction des catégories à partir des concepts wikipedia est le suivant. Cela pourrait être fait en utilisant pywikibot ou pymediawiki comme suit.

  1. Utilisation de la bibliothèque pymediawiki

    importer mediawiki en tant que pw

    p = wikipedia.page('enzyme inhibitor')
    print(p.categories)
    
  2. Utilisation de la bibliothèque pywikibot

    import pywikibot as pw
    
    site = pw.Site('en', 'wikipedia')
    
    print([
        cat.title()
        for cat in pw.Page(site, 'support-vector machine').categories()
        if 'hidden' not in cat.categoryinfo
    ])
    

Les catégories de catégories peuvent également être effectuées de la même manière que celle indiquée dans la réponse de @IlmariKaronen.

Si vous recherchez une liste plus longue de concepts pour les tests, j'ai mentionné plus d'exemples ci-dessous.

['juvenile chronic arthritis', 'climate', 'alexidine', 'mouthrinse', 'sialosis', 'australia', 'artificial neural network', 'ricinoleic acid', 'bromosulfophthalein', 'myelosclerosis', 'hydrochloride salt', 'cycasin', 'aldosterone antagonist', 'fungal growth', 'describe', 'liver resection', 'coffee table', 'natural language processing', 'infratemporal fossa', 'social withdrawal', 'information retrieval', 'monday', 'menthol', 'overturn', 'prevailing', 'spline function', 'acinic cell carcinoma', 'furth', 'hepatic protein', 'blistering', 'prefixation', 'january', 'cardiopulmonary receptor', 'extracorporeal membrane oxygenation', 'clinodactyly', 'melancholic', 'chlorpromazine hydrochloride', 'level of evidence', 'washington state', 'cat', 'newyork', 'year elevan', 'trituration', 'gold alloy', 'hexoprenaline', 'second molar', 'novice', 'oxygen radical', 'subscription', 'ordinate', 'approximal', 'spongiosis', 'ribothymidine', 'body of evidence', 'vpb', 'porins', 'musculocutaneous']

Pour une très longue liste, veuillez consulter le lien ci-dessous. https://docs.google.com/document/d/1BYllMyDlw-Rb4uMh89VjLml2Bl9Y7oUlopM-Z4F6pN0/edit?usp=sharing

REMARQUE: Je ne m'attends pas à ce que la solution fonctionne à 100% (si l'algorithme proposé est capable de détecter de nombreux concepts médicaux qui me suffisent)

Je suis heureux de fournir plus de détails si nécessaire.

17
EmJ

Vue d'ensemble de la solution

D'accord, j'aborderais le problème de plusieurs directions. Il y a de grandes suggestions ici et si j'étais vous, j'utiliserais un ensemble de ces approches (vote à la majorité, étiquette de prédiction acceptée par plus de 50% des classificateurs dans votre cas binaire).

Je pense aux approches suivantes:

  • Apprentissage actif (exemple d'approche fourni par moi ci-dessous)
  • Backlinks MediaWiki fourni comme réponse par @ TavoGC
  • [~ # ~] sparql [~ # ~] catégories ancestrales fournies en commentaire de votre question par @ Stanislav Kralin et/ou catégories parentes fourni par @ Meena Nagarajan (ces deux pourraient être un ensemble à part entière en fonction de leurs différences, mais pour cela, vous devrez contacter les deux créateurs et comparer leurs résultats).

De cette façon, 2 sur trois devraient convenir qu'un certain concept est médical, ce qui minimise davantage le risque d'erreur.

Pendant que nous y sommes, je dirais contre l'approche présentée par @ ananand_v.singh dans cette réponse , car:

  • la métrique de distance ne doit pas être euclidienne, la similitude en cosinus est une métrique bien meilleure (utilisée par, par exemple spaCy ) car elle ne prend pas en tenant compte de la magnitude des vecteurs (et cela ne devrait pas, c'est ainsi que Word2vec ou GloVe ont été formés)
  • de nombreux clusters artificiels seraient créés si je comprenais bien, alors que nous n'en avons besoin que de deux: un médicament et un non médical. De plus, le centre de gravité du médicament n'est pas centré sur le médicament lui-même. Cela pose des problèmes supplémentaires, par exemple le centroïde est éloigné du médicament et d'autres mots comme, par exemple, computer ou human (ou tout autre qui ne correspond pas à votre avis à la médecine) pourraient entrer en le cluster.
  • il est difficile d'évaluer les résultats, d'autant plus que la question est strictement subjective. De plus, les vecteurs Word sont difficiles à visualiser et à comprendre (les mouler dans des dimensions inférieures [2D/3D] en utilisant PCA/TSNE/similaire pour tant de mots, nous donneraient des résultats totalement insensibles [ouais, j'ai essayé de le faire, PCA obtient environ 5% de variance expliquée pour votre ensemble de données plus long, vraiment, vraiment faible]).

Sur la base des problèmes mis en évidence ci-dessus, j'ai trouvé une solution en utilisant apprentissage actif , ce qui est une approche assez oubliée de ces problèmes.

Approche d'apprentissage actif

Dans ce sous-ensemble de l'apprentissage automatique, lorsque nous avons du mal à trouver un algorithme exact (comme ce que cela signifie pour un terme de faire partie de la catégorie medical), nous demandons à un "expert" humain (doesn pas besoin d'être expert) pour apporter quelques réponses.

Encodage des connaissances

Comme anand_v.singh l'a souligné, les vecteurs Word sont l'une des approches les plus prometteuses et je vais également l'utiliser ici (différemment cependant, et IMO d'une manière beaucoup plus propre et plus facile).

Je ne vais pas répéter ses points dans ma réponse, je vais donc ajouter mes deux cents:

  • N'utilisez pas des intégrations de mots contextualisées comme état de la technique actuellement disponible (par exemple BERT )
  • Vérifiez combien de vos concepts n'ont aucune représentation (par exemple, est représenté comme un vecteur de zéros). Il doit être vérifié (et est vérifié dans mon code, il y aura d'autres discussions le moment venu) et vous pouvez utiliser l'incorporation qui a la plupart d'entre eux présents.

Mesurer la similitude en utilisant spaCy

Cette classe mesure la similitude entre medicine codé comme vecteur GloVe Word de spaCy et tout autre concept.

class Similarity:
    def __init__(self, centroid, nlp, n_threads: int, batch_size: int):
        # In our case it will be medicine
        self.centroid = centroid

        # spaCy's Language model (english), which will be used to return similarity to
        # centroid of each concept
        self.nlp = nlp
        self.n_threads: int = n_threads
        self.batch_size: int = batch_size

        self.missing: typing.List[int] = []

    def __call__(self, concepts):
        concepts_similarity = []
        # nlp.pipe is faster for many documents and can work in parallel (not blocked by GIL)
        for i, concept in enumerate(
            self.nlp.pipe(
                concepts, n_threads=self.n_threads, batch_size=self.batch_size
            )
        ):
            if concept.has_vector:
                concepts_similarity.append(self.centroid.similarity(concept))
            else:
                # If document has no vector, it's assumed to be totally dissimilar to centroid
                concepts_similarity.append(-1)
                self.missing.append(i)

        return np.array(concepts_similarity)

Ce code renverra un nombre pour chaque concept mesurant à quel point il est similaire au centroïde. De plus, il enregistre des indices de concepts manquant de leur représentation. Cela pourrait s'appeler comme ceci:

import json
import typing

import numpy as np
import spacy

nlp = spacy.load("en_vectors_web_lg")

centroid = nlp("medicine")

concepts = json.load(open("concepts_new.txt"))
concepts_similarity = Similarity(centroid, nlp, n_threads=-1, batch_size=4096)(
    concepts
)

Vous pouvez remplacer vos données à la place de new_concepts.json.

Regardez spacy.load et remarquez que j'ai utilisé en_vectors_web_lg . Il se compose de 685.000 vecteurs Word uniques (ce qui est beaucoup) et peut fonctionner hors de la boîte pour votre cas. Vous devez le télécharger séparément après l'installation de spaCy, plus d'informations fournies dans les liens ci-dessus.

De plus vous pouvez utiliser plusieurs mots centroïdes , par ex. ajoutez des mots comme disease ou health et faites la moyenne de leurs vecteurs Word. Je ne sais pas si cela affecterait positivement votre cas.

Une autre possibilité pourrait être d'utiliser plusieurs centroïdes et de calculer la similitude entre chaque concept et plusieurs centroïdes. Nous pouvons avoir quelques seuils dans un tel cas, cela supprimera probablement certains faux positifs , mais peut manquer certains termes que l'on pourrait considérer comme similaires à medicine. En outre, cela compliquerait beaucoup plus le cas, mais si vos résultats ne sont pas satisfaisants, vous devriez considérer deux options ci-dessus (et seulement si elles le sont, ne sautez pas dans cette approche sans y penser au préalable).

Maintenant, nous avons une mesure approximative de la similitude du concept. Mais qu'est-ce que cela signifie qu'un certain concept a 0,1 similitude positive avec la médecine? Est-ce un concept à classer comme médical? Ou peut-être que c'est déjà trop loin?

Demande d'expert

Pour obtenir un seuil (en dessous, les termes seront considérés comme non médicaux), il est plus facile de demander à un humain de classer certains des concepts pour nous (et c'est de cela qu'il s'agit pour l'apprentissage actif). Oui, je sais que c'est une forme très simple d'apprentissage actif, mais je le considérerais quand même.

J'ai écrit un cours avec sklearn-like interface demandant à l'homme de classer les concepts jusqu'à ce que le seuil optimal (ou le nombre maximal d'itérations) soit atteint.

class ActiveLearner:
    def __init__(
        self,
        concepts,
        concepts_similarity,
        max_steps: int,
        samples: int,
        step: float = 0.05,
        change_multiplier: float = 0.7,
    ):
        sorting_indices = np.argsort(-concepts_similarity)
        self.concepts = concepts[sorting_indices]
        self.concepts_similarity = concepts_similarity[sorting_indices]

        self.max_steps: int = max_steps
        self.samples: int = samples
        self.step: float = step
        self.change_multiplier: float = change_multiplier

        # We don't have to ask experts for the same concepts
        self._checked_concepts: typing.Set[int] = set()
        # Minimum similarity between vectors is -1
        self._min_threshold: float = -1
        # Maximum similarity between vectors is 1
        self._max_threshold: float = 1

        # Let's start from the highest similarity to ensure minimum amount of steps
        self.threshold_: float = 1
  • L'argument samples décrit le nombre d'exemples qui seront montrés à un expert à chaque itération (c'est le maximum, il retournera moins si des échantillons ont déjà été demandés ou s'il n'y en a pas assez à montrer).
  • step représente la baisse de seuil (on commence à 1 signifiant une similitude parfaite) à chaque itération.
  • change_multiplier - si un expert répond que les concepts ne sont pas liés (ou pour la plupart non liés, car plusieurs d'entre eux sont renvoyés), l'étape est multipliée par ce nombre à virgule flottante. Il est utilisé pour déterminer le seuil exact entre les changements de step à chaque itération.
  • les concepts sont triés en fonction de leur similitude (plus un concept est similaire, plus il est élevé)

La fonction ci-dessous demande l'avis d'un expert et trouve un seuil optimal en fonction de ses réponses.

def _ask_expert(self, available_concepts_indices):
    # Get random concepts (the ones above the threshold)
    concepts_to_show = set(
        np.random.choice(
            available_concepts_indices, len(available_concepts_indices)
        ).tolist()
    )
    # Remove those already presented to an expert
    concepts_to_show = concepts_to_show - self._checked_concepts
    self._checked_concepts.update(concepts_to_show)
    # Print message for an expert and concepts to be classified
    if concepts_to_show:
        print("\nAre those concepts related to medicine?\n")
        print(
            "\n".join(
                f"{i}. {concept}"
                for i, concept in enumerate(
                    self.concepts[list(concepts_to_show)[: self.samples]]
                )
            ),
            "\n",
        )
        return input("[y]es / [n]o / [any]quit ")
    return "y"

L'exemple de question ressemble à ceci:

Are those concepts related to medicine?                                                      

0. anesthetic drug                                                                                                                                                                         
1. child and adolescent psychiatry                                                                                                                                                         
2. tertiary care center                                                     
3. sex therapy                           
4. drug design                                                                                                                                                                             
5. pain disorder                                                      
6. psychiatric rehabilitation                                                                                                                                                              
7. combined oral contraceptive                                
8. family practitioner committee                           
9. cancer family syndrome                          
10. social psychology                                                                                                                                                                      
11. drug sale                                                                                                           
12. blood system                                                                        

[y]es / [n]o / [any]quit y

... analyser une réponse d'un expert:

# True - keep asking, False - stop the algorithm
def _parse_expert_decision(self, decision) -> bool:
    if decision.lower() == "y":
        # You can't go higher as current threshold is related to medicine
        self._max_threshold = self.threshold_
        if self.threshold_ - self.step < self._min_threshold:
            return False
        # Lower the threshold
        self.threshold_ -= self.step
        return True
    if decision.lower() == "n":
        # You can't got lower than this, as current threshold is not related to medicine already
        self._min_threshold = self.threshold_
        # Multiply threshold to pinpoint exact spot
        self.step *= self.change_multiplier
        if self.threshold_ + self.step < self._max_threshold:
            return False
        # Lower the threshold
        self.threshold_ += self.step
        return True
    return False

Et enfin tout le code du code de ActiveLearner, qui trouve un seuil de similitude optimal selon l'expert:

class ActiveLearner:
    def __init__(
        self,
        concepts,
        concepts_similarity,
        samples: int,
        max_steps: int,
        step: float = 0.05,
        change_multiplier: float = 0.7,
    ):
        sorting_indices = np.argsort(-concepts_similarity)
        self.concepts = concepts[sorting_indices]
        self.concepts_similarity = concepts_similarity[sorting_indices]

        self.samples: int = samples
        self.max_steps: int = max_steps
        self.step: float = step
        self.change_multiplier: float = change_multiplier

        # We don't have to ask experts for the same concepts
        self._checked_concepts: typing.Set[int] = set()
        # Minimum similarity between vectors is -1
        self._min_threshold: float = -1
        # Maximum similarity between vectors is 1
        self._max_threshold: float = 1

        # Let's start from the highest similarity to ensure minimum amount of steps
        self.threshold_: float = 1

    def _ask_expert(self, available_concepts_indices):
        # Get random concepts (the ones above the threshold)
        concepts_to_show = set(
            np.random.choice(
                available_concepts_indices, len(available_concepts_indices)
            ).tolist()
        )
        # Remove those already presented to an expert
        concepts_to_show = concepts_to_show - self._checked_concepts
        self._checked_concepts.update(concepts_to_show)
        # Print message for an expert and concepts to be classified
        if concepts_to_show:
            print("\nAre those concepts related to medicine?\n")
            print(
                "\n".join(
                    f"{i}. {concept}"
                    for i, concept in enumerate(
                        self.concepts[list(concepts_to_show)[: self.samples]]
                    )
                ),
                "\n",
            )
            return input("[y]es / [n]o / [any]quit ")
        return "y"

    # True - keep asking, False - stop the algorithm
    def _parse_expert_decision(self, decision) -> bool:
        if decision.lower() == "y":
            # You can't go higher as current threshold is related to medicine
            self._max_threshold = self.threshold_
            if self.threshold_ - self.step < self._min_threshold:
                return False
            # Lower the threshold
            self.threshold_ -= self.step
            return True
        if decision.lower() == "n":
            # You can't got lower than this, as current threshold is not related to medicine already
            self._min_threshold = self.threshold_
            # Multiply threshold to pinpoint exact spot
            self.step *= self.change_multiplier
            if self.threshold_ + self.step < self._max_threshold:
                return False
            # Lower the threshold
            self.threshold_ += self.step
            return True
        return False

    def fit(self):
        for _ in range(self.max_steps):
            available_concepts_indices = np.nonzero(
                self.concepts_similarity >= self.threshold_
            )[0]
            if available_concepts_indices.size != 0:
                decision = self._ask_expert(available_concepts_indices)
                if not self._parse_expert_decision(decision):
                    break
            else:
                self.threshold_ -= self.step
        return self

Dans l'ensemble, vous devrez répondre à certaines questions manuellement, mais cette approche est beaucoup plus précise à mon avis.

De plus, vous n'avez pas à parcourir tous les échantillons, juste un petit sous-ensemble. Vous pouvez décider combien d'échantillons constituent un terme médical (si 40 échantillons médicaux et 10 échantillons non médicaux doivent être considérés comme médicaux?), Ce qui vous permet d'affiner cette approche selon vos préférences. S'il y a une valeur aberrante (disons, 1 échantillon sur 50 n'est pas médical), je considérerais que le seuil est toujours valide.

Encore une fois: Cette approche doit être mélangée avec d'autres afin de minimiser les risques de mauvaise classification.

Classificateur

Lorsque nous obtenons le seuil de l'expert, la classification serait instantanée, voici une classe simple pour la classification:

class Classifier:
    def __init__(self, centroid, threshold: float):
        self.centroid = centroid
        self.threshold: float = threshold

    def predict(self, concepts_pipe):
        predictions = []
        for concept in concepts_pipe:
            predictions.append(self.centroid.similarity(concept) > self.threshold)
        return predictions

Et pour être bref, voici le code source final:

import json
import typing

import numpy as np
import spacy


class Similarity:
    def __init__(self, centroid, nlp, n_threads: int, batch_size: int):
        # In our case it will be medicine
        self.centroid = centroid

        # spaCy's Language model (english), which will be used to return similarity to
        # centroid of each concept
        self.nlp = nlp
        self.n_threads: int = n_threads
        self.batch_size: int = batch_size

        self.missing: typing.List[int] = []

    def __call__(self, concepts):
        concepts_similarity = []
        # nlp.pipe is faster for many documents and can work in parallel (not blocked by GIL)
        for i, concept in enumerate(
            self.nlp.pipe(
                concepts, n_threads=self.n_threads, batch_size=self.batch_size
            )
        ):
            if concept.has_vector:
                concepts_similarity.append(self.centroid.similarity(concept))
            else:
                # If document has no vector, it's assumed to be totally dissimilar to centroid
                concepts_similarity.append(-1)
                self.missing.append(i)

        return np.array(concepts_similarity)


class ActiveLearner:
    def __init__(
        self,
        concepts,
        concepts_similarity,
        samples: int,
        max_steps: int,
        step: float = 0.05,
        change_multiplier: float = 0.7,
    ):
        sorting_indices = np.argsort(-concepts_similarity)
        self.concepts = concepts[sorting_indices]
        self.concepts_similarity = concepts_similarity[sorting_indices]

        self.samples: int = samples
        self.max_steps: int = max_steps
        self.step: float = step
        self.change_multiplier: float = change_multiplier

        # We don't have to ask experts for the same concepts
        self._checked_concepts: typing.Set[int] = set()
        # Minimum similarity between vectors is -1
        self._min_threshold: float = -1
        # Maximum similarity between vectors is 1
        self._max_threshold: float = 1

        # Let's start from the highest similarity to ensure minimum amount of steps
        self.threshold_: float = 1

    def _ask_expert(self, available_concepts_indices):
        # Get random concepts (the ones above the threshold)
        concepts_to_show = set(
            np.random.choice(
                available_concepts_indices, len(available_concepts_indices)
            ).tolist()
        )
        # Remove those already presented to an expert
        concepts_to_show = concepts_to_show - self._checked_concepts
        self._checked_concepts.update(concepts_to_show)
        # Print message for an expert and concepts to be classified
        if concepts_to_show:
            print("\nAre those concepts related to medicine?\n")
            print(
                "\n".join(
                    f"{i}. {concept}"
                    for i, concept in enumerate(
                        self.concepts[list(concepts_to_show)[: self.samples]]
                    )
                ),
                "\n",
            )
            return input("[y]es / [n]o / [any]quit ")
        return "y"

    # True - keep asking, False - stop the algorithm
    def _parse_expert_decision(self, decision) -> bool:
        if decision.lower() == "y":
            # You can't go higher as current threshold is related to medicine
            self._max_threshold = self.threshold_
            if self.threshold_ - self.step < self._min_threshold:
                return False
            # Lower the threshold
            self.threshold_ -= self.step
            return True
        if decision.lower() == "n":
            # You can't got lower than this, as current threshold is not related to medicine already
            self._min_threshold = self.threshold_
            # Multiply threshold to pinpoint exact spot
            self.step *= self.change_multiplier
            if self.threshold_ + self.step < self._max_threshold:
                return False
            # Lower the threshold
            self.threshold_ += self.step
            return True
        return False

    def fit(self):
        for _ in range(self.max_steps):
            available_concepts_indices = np.nonzero(
                self.concepts_similarity >= self.threshold_
            )[0]
            if available_concepts_indices.size != 0:
                decision = self._ask_expert(available_concepts_indices)
                if not self._parse_expert_decision(decision):
                    break
            else:
                self.threshold_ -= self.step
        return self


class Classifier:
    def __init__(self, centroid, threshold: float):
        self.centroid = centroid
        self.threshold: float = threshold

    def predict(self, concepts_pipe):
        predictions = []
        for concept in concepts_pipe:
            predictions.append(self.centroid.similarity(concept) > self.threshold)
        return predictions


if __name__ == "__main__":
    nlp = spacy.load("en_vectors_web_lg")

    centroid = nlp("medicine")

    concepts = json.load(open("concepts_new.txt"))
    concepts_similarity = Similarity(centroid, nlp, n_threads=-1, batch_size=4096)(
        concepts
    )

    learner = ActiveLearner(
        np.array(concepts), concepts_similarity, samples=20, max_steps=50
    ).fit()
    print(f"Found threshold {learner.threshold_}\n")

    classifier = Classifier(centroid, learner.threshold_)
    pipe = nlp.pipe(concepts, n_threads=-1, batch_size=4096)
    predictions = classifier.predict(pipe)
    print(
        "\n".join(
            f"{concept}: {label}"
            for concept, label in Zip(concepts[20:40], predictions[20:40])
        )
    )

Après avoir répondu à quelques questions, avec le seuil 0,1 (tout entre [-1, 0.1) est considéré comme non médical, tandis que [0.1, 1] est considéré comme médical) J'ai obtenu les résultats suivants:

kartagener s syndrome: True
summer season: True
taq: False
atypical neuroleptic: True
anterior cingulate: False
acute respiratory distress syndrome: True
circularity: False
mutase: False
adrenergic blocking drug: True
systematic desensitization: True
the turning point: True
9l: False
pyridazine: False
bisoprolol: False
trq: False
propylhexedrine: False
type 18: True
darpp 32: False
rickettsia conorii: False
sport shoe: True

Comme vous pouvez le voir, cette approche est loin d'être parfaite, donc la dernière section décrit les améliorations possibles:

Améliorations possibles

Comme mentionné au début, l'utilisation de mon approche mélangée à d'autres réponses laisserait probablement de côté des idées comme sport shoe l'appartenance à medicine out et l'approche d'apprentissage actif constitueraient davantage un vote décisif en cas d'égalité entre deux heuristiques mentionnées ci-dessus.

Nous pourrions également créer un ensemble d'apprentissage actif. Au lieu d'un seuil, disons 0,1, nous en utiliserions plusieurs (soit croissants soit décroissants), disons que ce sont 0.1, 0.2, 0.3, 0.4, 0.5.

Disons sport shoe obtient, pour chaque seuil, il est respectif True/False comme ça:

True True False False False,

En faisant un vote majoritaire, nous le marquerions non-medical par 3 voix sur 2. En outre, un seuil trop strict serait également atténué si des seuils inférieurs à celui-ci l'emportaient (cas si True/False ressemblerait à ceci: True True True False False).

Amélioration finale possible J'ai trouvé : Dans le code ci-dessus, j'utilise le vecteur Doc, qui est un moyen de créer des vecteurs Word le concept. Supposons qu'un mot soit manquant (vecteurs composés de zéros), dans ce cas, il serait poussé plus loin du medicine centroïde. Vous ne voudrez peut-être pas que (comme certains termes médicaux de niche [des abréviations comme gpv ou autres] puissent ne pas être représentés), dans ce cas, vous ne pourrez faire la moyenne que des vecteurs différents de zéro.

Je sais que ce message est assez long, donc si vous avez des questions, postez-les ci-dessous.

6
Szymon Maszke

"Par conséquent, je voudrais savoir s'il existe un moyen d'obtenir le parent category des catégories (par exemple, les catégories de enzyme inhibitor et bypass surgery appartient à medical catégorie parent) "

Les catégories MediaWiki sont elles-mêmes des pages wiki. Une "catégorie parent" est juste une catégorie à laquelle appartient la page de catégorie "enfant". Ainsi, vous pouvez obtenir les catégories parentes d'une catégorie exactement de la même manière que vous obtiendrez les catégories de n'importe quelle autre page wiki.

Par exemple, en utilisant pymediawiki :

p = wikipedia.page('Category:Enzyme inhibitors')
parents = p.categories
6
Ilmari Karonen

Vous pouvez essayer de classer les catégories wikipedia par les liens et backlinks mediawiki renvoyés pour chaque catégorie

import re
from mediawiki import MediaWiki

#TermFind will search through a list a given term
def TermFind(term,termList):
    responce=False
    for val in termList:
        if re.match('(.*)'+term+'(.*)',val):
            responce=True
            break
    return responce

#Find if the links and backlinks lists contains a given term 
def BoundedTerm(wikiPage,term):
    aList=wikiPage.links
    bList=wikiPage.backlinks
    responce=False
    if TermFind(term,aList)==True and TermFind(term,bList)==True:
         responce=True
    return responce

container=[]
wikipedia = MediaWiki()
for val in termlist:
    cpage=wikipedia.page(val)
    if BoundedTerm(cpage,'term')==True:
        container.append('medical')
    else:
        container.append('nonmedical')

L'idée est d'essayer de deviner un terme qui est partagé par la plupart des catégories, j'essaie la biologie, la médecine et la maladie avec de bons résultats. Vous pouvez peut-être essayer d'utiliser plusieurs appels de BoundedTerms pour effectuer la classification, ou un seul appel pour plusieurs termes et combiner le résultat pour la classification. J'espère que ça aide

4
TavoGLC

Il y a un concept de vecteurs de mots dans la PNL, ce qu'il fait essentiellement en regardant à travers des volumes massifs de texte, il essaie de convertir les mots en vecteurs multidimensionnels, puis de réduire la distance entre ces vecteurs, plus la similitude entre eux, la bonne chose est que beaucoup de gens ont déjà généré ces vecteurs Word et les ont rendus disponibles sous des licences très permissives, et dans votre cas, vous travaillez avec Wikipedia et il existe des vecteurs Word pour eux ici http://dumps.wikimedia.org /enwiki/latest/enwiki-latest-pages-articles.xml.bz2

Maintenant, ceux-ci seraient les plus adaptés à cette tâche car ils contiennent la plupart des mots des corpus de Wikipédia, mais au cas où ils ne vous conviendraient pas, ou s'ils sont supprimés à l'avenir, vous pouvez en utiliser un à partir de la liste ci-dessous, avec cela dit, il y a une meilleure façon de le faire, c'est-à-dire en les passant au module de langage universel de tensorflow embed module dans lequel vous n'avez pas à faire la plupart du travail lourd, vous pouvez en savoir plus à ce sujet - ici. La raison pour laquelle je l'ai mis après le vidage de texte Wikipedia est parce que j'ai entendu des gens dire qu'ils sont un peu difficiles à travailler lorsqu'ils travaillent avec des échantillons médicaux. Cet article propose une solution pour résoudre ce problème, mais je n'ai jamais essayé cela, donc je ne peux pas être sûr de sa précision.

Maintenant, comment utiliser les intégrations Word à partir de tensorflow est simple, faites simplement

embed = hub.Module("https://tfhub.dev/google/universal-sentence-encoder/2")
embeddings = embed(["Input Text here as"," List of strings"])
session.run(embeddings)

Étant donné que vous ne connaissez peut-être pas tensorflow et que vous n'essayez d'exécuter que ce morceau de code, vous pouvez rencontrer des problèmes, Suivez ce lien où ils ont complètement expliqué comment l'utiliser et à partir de là, vous devriez pouvoir pour l'adapter facilement à vos besoins.

Cela dit, je recommanderais d'abord de vérifier le module d'intégration de tensorlfow et leurs intégrations Word pré-formées, s'ils ne fonctionnent pas pour vous, consultez le lien Wikimedia, si cela ne fonctionne pas, passez aux concepts de l'article. J'ai lié. Étant donné que cette réponse décrit une approche PNL, elle ne sera pas exacte à 100%, alors gardez cela à l'esprit avant de continuer.

Vecteurs de gants https://nlp.stanford.edu/projects/glove/

Texte rapide de Facebook: https://github.com/facebookresearch/fastText/blob/master/pretrained-vectors.md

Ou ceci http://www.statmt.org/lm-benchmark/1-billion-Word-language-modeling-benchmark-r13output.tar.gz

Si vous rencontrez des problèmes lors de l'implémentation de ceci après avoir suivi le tutoriel colab ajoutez votre problème à la question et au commentaire ci-dessous, à partir de là, nous pouvons continuer.

Modifier le code ajouté aux sujets du cluster

Bref, Plutôt que d'utiliser des mots vectoriels, j'encode leurs phrases sommaires

fichier content.py

def AllTopics():
    topics = []# list all your topics, not added here for space restricitons
    for i in range(len(topics)-1):
        yield topics[i]

Résumé du fichier Generator.py

import wikipedia
import pickle
from content import Alltopics
summary = []
failed = []
for topic in Alltopics():
    try:
        summary.append(wikipedia.summary(Tuple((topic,str(topic)))))
    except Exception as e:
        failed.append(Tuple((topic,e)))
with open("summary.txt", "wb") as fp:
    pickle.dump(summary , fp)
with open('failed.txt', 'wb') as fp:
    pickle.dump('failed', fp)

Fichier SimilartiyCalculator.py

import tensorflow as tf
import tensorflow_hub as hub
import numpy as np
import os
import pandas as pd
import re
import pickle
import sys
from sklearn.cluster import AgglomerativeClustering
from sklearn import metrics
from scipy.cluster import hierarchy
from scipy.spatial import distance_matrix


try:
    with open("summary.txt", "rb") as fp:   # Unpickling
        summary = pickle.load(fp)
except Exception as e:
    print ('Cannot load the summary file, Please make sure that it exists, if not run Summary Generator first', e)
    sys.exit('Read the error message')

module_url = "https://tfhub.dev/google/universal-sentence-encoder-large/3"
embed = hub.Module(module_url)

tf.logging.set_verbosity(tf.logging.ERROR)
messages = [x[1] for x in summary]
labels = [x[0] for x in summary]
with tf.Session() as session:
    session.run([tf.global_variables_initializer(), tf.tables_initializer()])
    message_embeddings = session.run(embed(messages)) # In message embeddings each vector is a second (1,512 vector) and is numpy.ndarray (noOfElemnts, 512)

X = message_embeddings
agl = AgglomerativeClustering(n_clusters=5, affinity='euclidean', memory=None, connectivity=None, compute_full_tree='auto', linkage='ward', pooling_func='deprecated')
agl.fit(X)
dist_matrix = distance_matrix(X,X)
Z = hierarchy.linkage(dist_matrix, 'complete')
dendro = hierarchy.dendrogram(Z)
cluster_labels = agl.labels_

Ceci est également hébergé sur GitHub à https://github.com/anandvsingh/WikipediaSimilarity Où vous pouvez trouver le similarity.txt fichier, et d'autres fichiers, Dans mon cas, je ne pouvais pas l'exécuter sur tous les sujets, mais je vous exhorte à l'exécuter sur la liste complète des sujets (cloner directement le référentiel et exécuter SummaryGenerator.py), et téléchargez le similarity.txt via une pull request au cas où vous n'obtiendriez pas le résultat attendu. Et si possible, téléchargez également le message_embeddings dans un fichier csv en tant que rubriques et intégrations.

Modifications après la modification 2 Commutation du similarityGenerator vers un clustering basé sur la hiérarchie (Agglomératif) Je vous suggère de conserver les noms de titre au bas du dendrogramme et pour qui regardent la définition de dendrogramme ici , j'ai vérifié la visualisation de certains échantillons et les résultats semblent assez bons, vous pouvez changer le n_clusters valeur pour affiner votre modèle. Remarque: Cela vous oblige à réexécuter le générateur de résumé. Je pense que vous devriez pouvoir le prendre à partir d'ici, ce que vous devez faire est d'essayer quelques valeurs de n_cluster et voyez dans lequel tous les termes médicaux sont regroupés, puis trouvez le cluster_label pour ce cluster et vous avez terminé. Puisque nous regroupons ici par résumé, les clusters seront plus précis. Si vous rencontrez des problèmes ou ne comprenez pas quelque chose, commentez ci-dessous.

4
anand_v.singh

La bibliothèque wikipedia est également un bon pari pour extraire les catégories d'une page donnée, car wikipedia.WikipediaPage(page).categories renvoie une liste simple. La bibliothèque vous permet également de rechercher plusieurs pages si elles ont toutes le même titre.

En médecine, il semble y avoir beaucoup de racines et de suffixes clés, donc l'approche de la recherche de mots clés peut être une bonne approche pour trouver des termes médicaux.

import wikipedia

def categorySorter(targetCats, pagesToCheck, mainCategory):
    targetList = []
    nonTargetList = []
    targetCats = [i.lower() for i in targetCats]

    print('Sorting pages...')
    print('Sorted:', end=' ', flush=True)
    for page in pagesToCheck:

        e = openPage(page)

        def deepList(l):
            for item in l:
                if item[1] == 'SUBPAGE_ID':
                    deepList(item[2])
                else:
                    catComparator(item[0], item[1], targetCats, targetList, nonTargetList, pagesToCheck[-1])

        if e[1] == 'SUBPAGE_ID':
            deepList(e[2])
        else:
            catComparator(e[0], e[1], targetCats, targetList, nonTargetList, pagesToCheck[-1])

    print()
    print()
    print('Results:')
    print(mainCategory, ': ', targetList, sep='')
    print()
    print('Non-', mainCategory, ': ', nonTargetList, sep='')

def openPage(page):
    try:
        pageList = [page, wikipedia.WikipediaPage(page).categories]
    except wikipedia.exceptions.PageError as p:
        pageList = [page, 'NONEXIST_ID']
        return
    except wikipedia.exceptions.DisambiguationError as e:
        pageCategories = []
        for i in e.options:
            if '(disambiguation)' not in i:
                pageCategories.append(openPage(i))
        pageList = [page, 'SUBPAGE_ID', pageCategories]
        return pageList
    finally:
        return pageList

def catComparator(pageTitle, pageCategories, targetCats, targetList, nonTargetList, lastPage):

    # unhash to view the categories of each page
    #print(pageCategories)
    pageCategories = [i.lower() for i in pageCategories]

    any_in = False
    for i in targetCats:
        if i in pageTitle:
            any_in = True
    if any_in:
        print('', end = '', flush=True)
    Elif compareLists(targetCats, pageCategories):
        any_in = True

    if any_in:
        targetList.append(pageTitle)
    else:
        nonTargetList.append(pageTitle)

    # Just prints a pretty list, you can comment out until next hash if desired
    if any_in:
        print(pageTitle, '(T)', end='', flush=True)
    else:
        print(pageTitle, '(F)',end='', flush=True)

    if pageTitle != lastPage:
        print(',', end=' ')
    # No more commenting

    return any_in

def compareLists (a, b):
    for i in a:
        for j in b:
            if i in j:
                return True
    return False

Le code compare simplement une liste de mots clés et de suffixes aux titres de chaque page ainsi qu'à leurs catégories pour déterminer si une page est médicalement liée. Il examine également les pages/sous-pages associées pour les sujets plus importants et détermine si elles sont également liées. Je ne connais pas très bien ma médecine alors pardonnez les catégories mais voici un exemple à taguer en bas:

medicalCategories = ['surgery', 'medic', 'disease', 'drugs', 'virus', 'bact', 'fung', 'pharma', 'cardio', 'pulmo', 'sensory', 'nerv', 'derma', 'protein', 'amino', 'unii', 'chlor', 'carcino', 'oxi', 'oxy', 'sis', 'disorder', 'enzyme', 'eine', 'sulf']
listOfPages = ['juvenile chronic arthritis', 'climate', 'alexidine', 'mouthrinse', 'sialosis', 'australia', 'artificial neural network', 'ricinoleic acid', 'bromosulfophthalein', 'myelosclerosis', 'hydrochloride salt', 'cycasin', 'aldosterone antagonist', 'fungal growth', 'describe', 'liver resection', 'coffee table', 'natural language processing', 'infratemporal fossa', 'social withdrawal', 'information retrieval', 'monday', 'menthol', 'overturn', 'prevailing', 'spline function', 'acinic cell carcinoma', 'furth', 'hepatic protein', 'blistering', 'prefixation', 'january', 'cardiopulmonary receptor', 'extracorporeal membrane oxygenation', 'clinodactyly', 'melancholic', 'chlorpromazine hydrochloride', 'level of evidence', 'washington state', 'cat', 'year elevan', 'trituration', 'gold alloy', 'hexoprenaline', 'second molar', 'novice', 'oxygen radical', 'subscription', 'ordinate', 'approximal', 'spongiosis', 'ribothymidine', 'body of evidence', 'vpb', 'porins', 'musculocutaneous']
categorySorter(medicalCategories, listOfPages, 'Medical')

Cet exemple de liste obtient ~ 70% de ce qui devrait être sur la liste, du moins à ma connaissance.

4
Londala

La question me semble un peu floue et ne semble pas être un problème simple à résoudre et peut nécessiter un modèle de PNL. En outre, les mots concept et catégories sont interchangeables. Ce que je comprends, c'est que les concepts tels que l'inhibiteur d'enzyme, la chirurgie de pontage et l'hypertriglycéridimie doivent être combinés ensemble comme médical et le reste comme non médical. Ce problème nécessitera plus de données que les noms de catégorie. Un corpus est nécessaire pour former un modèle LDA (par exemple) où toutes les informations textuelles sont transmises à l'algorithme et retournent les sujets les plus probables pour chacun des concepts.

https://www.analyticsvidhya.com/blog/2018/10/stepwise-guide-topic-modeling-latent-semantic-analysis/

3
Meena Nagarajan