2 votes

Comment spacy pourrait-il symboliser le hashtag dans son ensemble ?

Dans une phrase contenant des hashtags, comme un tweet, le tokenizer de spacy sépare les hashtags en deux tokens :

import spacy
nlp = spacy.load('en')
doc = nlp(u'This is a #sentence.')
[t for t in doc]

de la production :

[This, is, a, #, sentence, .]

J'aimerais que les hashtags soient considérés comme tels :

[This, is, a, #sentence, .]

Est-ce possible ?

Remerciements

2voto

DhruvPathak Points 16181
  1. Vous pouvez effectuer des manipulations avant et après la chaîne de caractères, ce qui vous permettra de contourner la symbolisation basée sur le "#", et est facile à mettre en œuvre.
> >>> import re
> >>> import spacy
> >>> nlp = spacy.load('en')
> >>> sentence = u'This is my twitter update #MyTopic'
> >>> parsed = nlp(sentence)
> >>> [token.text for token in parsed]
 [u'This', u'is', u'my', u'twitter', u'update', u'#', u'MyTopic']
> >>> new_sentence = re.sub(r'#(\w+)',r'ZZZPLACEHOLDERZZZ\1',sentence) 
> >>> new_sentence u'This is my twitter update ZZZPLACEHOLDERZZZMyTopic'
> >>> parsed = nlp(new_sentence)
> >>> [token.text for token in parsed]
 [u'This', u'is', u'my', u'twitter', u'update', u'ZZZPLACEHOLDERZZZMyTopic']
> >>> [x.replace(u'ZZZPLACEHOLDERZZZ','#') for x in [token.text for token in parsed]]
 [u'This', u'is', u'my', u'twitter', u'update', u'#MyTopic']
  1. Vous pouvez essayer de définir des séparateurs personnalisés dans le tokenizer de spacy. Je n'ai pas connaissance de méthodes permettant de le faire.

UPDATE : Vous pouvez utiliser une expression rationnelle pour trouver la plage de jetons que vous souhaitez conserver en tant que jeton unique, et recréer un jeton à l'aide de la méthode span.merge, comme indiqué ici : https://spacy.io/docs/api/span#merge

Exemple de fusion :

>>> import spacy
>>> import re
>>> nlp = spacy.load('en')
>>> my_str = u'Tweet hashtags #MyHashOne #MyHashTwo'
>>> parsed = nlp(my_str)
>>> [(x.text,x.pos_) for x in parsed]
[(u'Tweet', u'PROPN'), (u'hashtags', u'NOUN'), (u'#', u'NOUN'), (u'MyHashOne', u'NOUN'), (u'#', u'NOUN'), (u'MyHashTwo', u'PROPN')]
>>> indexes = [m.span() for m in re.finditer('#\w+',my_str,flags=re.IGNORECASE)]
>>> indexes
[(15, 25), (26, 36)]
>>> for start,end in indexes:
...     parsed.merge(start_idx=start,end_idx=end)
... 
#MyHashOne
#MyHashTwo
>>> [(x.text,x.pos_) for x in parsed]
[(u'Tweet', u'PROPN'), (u'hashtags', u'NOUN'), (u'#MyHashOne', u'NOUN'), (u'#MyHashTwo', u'PROPN')]
>>>

1voto

Moritz Points 98

Il s'agit plutôt d'une complément à l'excellente réponse de @DhruvPathak ET à une copie éhontée du fil de discussion github ci-dessous (et de la réponse encore meilleure de @csvance). caractéristiques du spaCy (depuis V2.0) le add_pipe méthode . Cela signifie que vous pouvez définir la grande réponse de @DhruvPathak dans une fonction et ajouter l'étape (de manière pratique) dans votre pipeline de traitement nlp, comme ci-dessous.

Les citations commencent ici :

def hashtag_pipe(doc):
    merged_hashtag = False
    while True:
        for token_index,token in enumerate(doc):
            if token.text == '#':
                if token.head is not None:
                    start_index = token.idx
                    end_index = start_index + len(token.head.text) + 1
                    if doc.merge(start_index, end_index) is not None:
                        merged_hashtag = True
                        break
        if not merged_hashtag:
            break
        merged_hashtag = False
    return doc

nlp = spacy.load('en')
nlp.add_pipe(hashtag_pipe)

doc = nlp("twitter #hashtag")
assert len(doc) == 2
assert doc[0].text == 'twitter'
assert doc[1].text == '#hashtag'

La citation s'arrête ici ; Vérifier comment ajouter des hashtags au tagger de parties du discours #503 pour l'intégralité du fil de discussion.

PS C'est clair à la lecture du code, mais pour les copy&pasters, ne désactivez pas l'analyseur :)

0voto

Ali Abul Hawa Points 136

J'ai trouvé ceci sur github qui utilise le système de Matcher :

from spacy.matcher import Matcher

matcher = Matcher(nlp.vocab)
matcher.add('HASHTAG', None, [{'ORTH': '#'}, {'IS_ASCII': True}])

doc = nlp('This is a #sentence. Here is another #hashtag. #The #End.')
matches = matcher(doc)
hashtags = []
for match_id, start, end in matches:
    hashtags.append(doc[start:end])

for span in hashtags:
    span.merge()

print([t.text for t in doc])

des sorties :

['This', 'is', 'a', '#sentence', '.', 'Here', 'is', 'another', '#hashtag', '.', '#The', '#End', '.']

Une liste de hashtags est également disponible dans la rubrique hashtags liste :

print(hashtags)

de la production :

[#sentence, #hashtag, #The, #End]

0voto

Flurin Gishamer Points 63

J'ai passé pas mal de temps sur ce sujet et j'ai décidé de partager ce que j'ai trouvé : Sous-classer le Tokenizer et ajouter la regex pour les hashtags à l'URL_PATTERN par défaut était la solution la plus facile pour moi, en plus d'ajouter une extension personnalisée pour correspondre aux hashtags afin de les identifier :

import re
import spacy
from spacy.language import Language
from spacy.tokenizer import Tokenizer
from spacy.tokens import Token

nlp = spacy.load('en_core_web_sm')

def create_tokenizer(nlp):
    # contains the regex to match all sorts of urls:
    from spacy.lang.tokenizer_exceptions import URL_PATTERN

    # spacy defaults: when the standard behaviour is required, they
    # need to be included when subclassing the tokenizer
    prefix_re = spacy.util.compile_prefix_regex(Language.Defaults.prefixes)
    infix_re = spacy.util.compile_infix_regex(Language.Defaults.infixes)
    suffix_re = spacy.util.compile_suffix_regex(Language.Defaults.suffixes)

    # extending the default url regex with regex for hashtags with "or" = |
    hashtag_pattern = r'''|^(#[\w_-]+)$'''
    url_and_hashtag = URL_PATTERN + hashtag_pattern
    url_and_hashtag_re = re.compile(url_and_hashtag)

    # set a custom extension to match if token is a hashtag
    hashtag_getter = lambda token: token.text.startswith('#')
    Token.set_extension('is_hashtag', getter=hashtag_getter)

    return Tokenizer(nlp.vocab, prefix_search=prefix_re.search,
                     suffix_search=suffix_re.search,
                     infix_finditer=infix_re.finditer,
                     token_match=url_and_hashtag_re.match
                     )

nlp.tokenizer = create_tokenizer(nlp)
doc = nlp("#spreadhappiness #smilemore so_great@good.com https://www.somedomain.com/foo")

for token in doc:
    print(token.text)
    if token._.is_hashtag:
        print("-> matches hashtag")

# returns: "#spreadhappiness -> matches hashtag #smilemore -> matches hashtag so_great@good.com https://www.somedomain.com/foo"

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X