Как получить текст корпуса википедии с пунктуацией с помощью gensim wikicorpus?

Я пытаюсь получить текст с его пунктуацией, так как важно учитывать последнюю в моей модели doc2vec. Однако викикорпус извлекает только текст. После поиска в Интернете я нашел эти страницы:

  1. Страница из раздела проблем gensim на github. Это был вопрос кого-то, ответом на который был подкласс WikiCorpus (отвечает Пискворкий). К счастью, на той же странице был код, представляющий предлагаемое решение «подкласса». Код был предоставлен Rhazegh. (ссылка)
  2. Страница из stackoverflow с заголовком: «Отключение удаления пунктуации и т. Д. Gensim при анализе вики-корпуса». Однако четкого ответа не было дано и рассматривалось в контексте spaCy. (ссылка)

Я решил использовать код, представленный на странице 1. Мой текущий код (mywikicorpus.py):

import sys
import os
sys.path.append('C:\\Users\\Ghaliamus\\Anaconda2\\envs\\wiki\\Lib\\site-packages\\gensim\\corpora\\')

from wikicorpus import *

def tokenize(content):
    # override original method in wikicorpus.py
    return [token.encode('utf8') for token in utils.tokenize(content, lower=True, errors='ignore')
        if len(token) <= 15 and not token.startswith('_')]

def process_article(args):
   # override original method in wikicorpus.py
    text, lemmatize, title, pageid = args
    text = filter_wiki(text)
    if lemmatize:
        result = utils.lemmatize(text)
    else:
        result = tokenize(text)
    return result, title, pageid


class MyWikiCorpus(WikiCorpus):
def __init__(self, fname, processes=None, lemmatize=utils.has_pattern(), dictionary=None, filter_namespaces=('0',)):
    WikiCorpus.__init__(self, fname, processes, lemmatize, dictionary, filter_namespaces)

    def get_texts(self):
        articles, articles_all = 0, 0
        positions, positions_all = 0, 0
        texts = ((text, self.lemmatize, title, pageid) for title, text, pageid in extract_pages(bz2.BZ2File(self.fname), self.filter_namespaces))
        pool = multiprocessing.Pool(self.processes)
        for group in utils.chunkize(texts, chunksize=10 * self.processes, maxsize=1):
            for tokens, title, pageid in pool.imap(process_article, group):  # chunksize=10):
                articles_all += 1
                positions_all += len(tokens)
            if len(tokens) < ARTICLE_MIN_WORDS or any(title.startswith(ignore + ':') for ignore in IGNORED_NAMESPACES):
                continue
            articles += 1
            positions += len(tokens)
            if self.metadata:
                yield (tokens, (pageid, title))
            else:
                yield tokens
    pool.terminate()

    logger.info(
        "finished iterating over Wikipedia corpus of %i documents with %i positions"
        " (total %i articles, %i positions before pruning articles shorter than %i words)",
        articles, positions, articles_all, positions_all, ARTICLE_MIN_WORDS)
    self.length = articles  # cache corpus length

Затем я использовал другой код Пан Яна (ссылка). Этот код инициирует объект WikiCorpus и извлекает текст. Единственное изменение в моем текущем коде — запуск MyWikiCorpus вместо WikiCorpus. Код (process_wiki.py):

from __future__ import print_function
import logging
import os.path
import six
import sys
import mywikicorpus as myModule



if __name__ == '__main__':
    program = os.path.basename(sys.argv[0])
    logger = logging.getLogger(program)

    logging.basicConfig(format='%(asctime)s: %(levelname)s: %(message)s')
    logging.root.setLevel(level=logging.INFO)
    logger.info("running %s" % ' '.join(sys.argv))

    # check and process input arguments
    if len(sys.argv) != 3:
        print("Using: python process_wiki.py enwiki-20180601-pages-    articles.xml.bz2 wiki.en.text")
        sys.exit(1)
    inp, outp = sys.argv[1:3]
    space = " "
    i = 0

    output = open(outp, 'w')
    wiki = myModule.MyWikiCorpus(inp, lemmatize=False, dictionary={})
    for text in wiki.get_texts():
        if six.PY3:
            output.write(bytes(' '.join(text), 'utf-8').decode('utf-8') + '\n')
        else:
            output.write(space.join(text) + "\n")
        i = i + 1
        if (i % 10000 == 0):
            logger.info("Saved " + str(i) + " articles")

    output.close()
    logger.info("Finished Saved " + str(i) + " articles")

Через командную строку я запустил код process_wiki.py. Я получил текст корпуса с последней строкой в ​​командной строке:

(2018-06-05 09:18:16,480: ИНФОРМАЦИЯ: Готово Сохранено 4526191 статьи)

Когда я прочитал файл на питоне, я проверил первую статью, и она была без знаков препинания. Пример:

(анархизм — это политическая философия, отстаивающая самоуправляемые общества, основанные на добровольных институтах, которые часто описываются как общества без государства, хотя некоторые авторы определяют их более конкретно как институты, основанные на неиерархических или свободных ассоциациях. Анархизм считает государство нежелательным, ненужным и вредным, в то время как противопоставление государству является центральным, анархизм конкретно влечет за собой противодействие власти или иерархии)

Два моих актуальных вопроса, и я хотел бы, чтобы вы помогли мне с ними, пожалуйста:

  1. что-то не так в моем указанном конвейере выше?
  2. независимо от такого конвейера, если я открою код Python gensim wikicorpus (wikicorpus.py) и хотел отредактировать его, какую строку мне следует добавить, удалить или обновить (что, если возможно), чтобы получить те же результаты, но с пунктуацией?

Большое спасибо, что уделили время чтению этого длинного поста.

С наилучшими пожеланиями,

Галиамус


person Ghaliamus    schedule 05.06.2018    source источник


Ответы (2)


Проблема заключается в вашей определенной функции токенизации

def tokenize(content):
    return [token.encode('utf8') for token in utils.tokenize(content, 
            lower=True, errors='ignore') if len(token) <= 15 and not 
            token.startswith('_')]

Функция utils.tokenize(content, lower=True, errors='ignore') просто преобразует статью в список токенов. Однако реализация этой функции в .../site-packages/gensim/utils.py игнорирует пунктуацию.

Например, когда вы вызываете utils.tokenize("Я люблю есть бананы, яблоки"), он возвращает ["Я", "люблю", "есть", "банан", "яблоко"]

В любом случае, вы можете определить свою собственную функцию токенизации следующим образом, чтобы сохранить пунктуацию.

def tokenize(content):
    #override original method in wikicorpus.py
    return [token.encode('utf8') for token in content.split() 
           if len(token) <= 15 and not token.startswith('_')]
person Mai Long    schedule 13.07.2018
comment
Хотя этот код может ответить на вопрос, предоставление информации о том, как и почему он решает проблему, повышает его ценность в долгосрочной перспективе. - person L_J; 13.07.2018
comment
Пожалуйста, добавьте некоторые пояснения. - person petezurich; 13.07.2018
comment
Большое спасибо, Май, за ответ на мой вопрос. Было бы любезно с вашей стороны, если бы вы объяснили ответ, как предложил L_J. - person Ghaliamus; 15.07.2018
comment
дополнительные пояснения см. здесь: github.com/RaRe-Technologies/ gensim/проблемы/ - person Ali Abul Hawa; 10.03.2019

В gensim/utils.py вы найдете метод

def save_as_line_sentence(corpus, filename):
    with smart_open(filename, mode='wb', encoding='utf8') as fout:
        for sentence in corpus:
            line = any2unicode(' '.join(sentence) + '\n')
            fout.write(line)

который вы можете использовать для записи корпуса в текстовый файл. Вы можете переопределить его или взять в качестве примера и написать свою собственную версию (возможно, вы хотите разбить строки на каждой пунктуации), например

def save_sentence_each_line(corpus, filename):
    with utils.smart_open(filename, mode='wb', encoding='utf8') as fout:
        for sentence in corpus:
            line = utils.any2unicode(' '.join(sentence) + '\n')
            line = line.replace('. ', '\n').replace('!', '\n').replace('?', '\n') # <- !!
            ...

вы можете назвать это как

save_sentence_each_line(wiki.get_texts(), out_f)

но вам также нужно переопределить PAT_ALPHABETIC из utils, потому что здесь удаляются знаки препинания:

PAT_ALPHABETIC = re.compile(r'(((?![\d])[\w\\.\\!\\?])+)', re.UNICODE)

Затем вам может потребоваться переопределить utils.tokenize и utils.simple_tokenize, если вы захотите внести дополнительные изменения в код.

person headkit    schedule 14.03.2019