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.
['Category:Lipid metabolism disorders', 'Category:Medical conditions related to obesity']
['Category:Enzyme inhibitors', 'Category:Medicinal chemistry', 'Category:Metabolism']
['Category:Surgery stubs', 'Category:Surgical procedures and techniques']
['Category:1829 establishments in Australia', 'Category:Australian capital cities', 'Category:Metropolitan areas of Australia', 'Category:Perth, Western Australia', 'Category:Populated places established in 1829']
['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
).
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.
Utilisation de la bibliothèque pymediawiki
importer mediawiki en tant que pw
p = wikipedia.page('enzyme inhibitor')
print(p.categories)
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.
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:
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:
computer
ou human
(ou tout autre qui ne correspond pas à votre avis à la médecine) pourraient entrer en le cluster.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.
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.
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:
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?
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
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.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.
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:
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.
"Par conséquent, je voudrais savoir s'il existe un moyen d'obtenir le
parent category
des catégories (par exemple, les catégories deenzyme inhibitor
etbypass 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
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
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.
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.
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/