def _parse_worker(self, from_queue: Queue, to_queue: Queue): splitter = SentenceSplitter(self.lang) while True: # Get raw-formatted document from main process. document = from_queue.get() if document is None: to_queue.put(None) break # Parse the document to the plain text. parsed = self.parser.parse(document) # Divide the document into sequences with required length. group_sentences = [] for paragraph in parsed.splitlines(): for sentence in splitter.tokenize(paragraph): group_sentences.append(sentence) if sum(len(s) for s in group_sentences) > self.max_len: to_queue.put(' '.join(group_sentences)) group_sentences.clear() # Use custom line-break token instead of `\n` which is used for # separating sequences. if group_sentences: group_sentences.append(self.newline) # Use the remainder in dataset if its length is suitable. if group_sentences and group_sentences[-1] == self.newline: group_sentences = group_sentences[:-1] text = ' '.join(group_sentences) if len(text) > self.min_len and len(text) < self.max_len: to_queue.put(text)
def __init__(self, parser: Parser, lang: str, min_len: int, max_len: int, newline: str = '[NEWLINE]', num_workers: int = 1): self.parser = parser self.min_len = min_len self.max_len = max_len self.newline = newline self.num_workers = num_workers self.splitter = SentenceSplitter(lang)
def test_korean_sentence_splitting_in_multiprocessing(): _dummy_sentence_corpus = ('위키백과 또는 위키피디아는 누구나 자유롭게 쓸 수 있는 다언어판 인터넷 ' '백과사전이다. 2001년 1월 15일 지미 웨일스와 래리 생어가 시작하였으며, ' '대표적인 집단 지성의 사례로 평가받고 있다. 위키백과는 자유 저작물을 ' '보유하고 상업적인 광고가 없으며 주로 기부금을 통해 지원을 받는 비영리 ' '단체인 위키미디어 재단에 의해 소유되고 지원을 받고 있다. 2020년 기준으로 ' '영어판 600만여 개, 한국어판 517,949개를 비롯하여 300여 언어판을 합하면 ' '4천만 개 이상의 글이 수록되어 꾸준히 성장하고 있으며 앞으로 더 성장할 ' '예정이다. 위키백과의 저작권은 크리에이티브 커먼즈 라이선스(CCL)와 GNU ' '자유 문서(GFDL)의 2중 라이선스를 따른다. 두 라이선스 모두 자유 콘텐츠를 ' '위한 것으로 일정한 요건을 갖추면 사용에 제약을 받지 않는다.') # Create worker process function to split sentences in multi-processing. def _process(text: str, splitter: SentenceSplitter, queue: Queue): queue.put(splitter.tokenize(text)) # Prepare processes and queue to communicate with them. queue = Queue() splitter = SentenceSplitter('ko') workers = [ Process(target=_process, args=(_dummy_sentence_corpus, splitter, queue), daemon=True) for _ in range(10) ] # Start the processes. for w in workers: w.start() # Check if all processes split sentences correctly. for _ in range(10): assert (queue.get() == [ '위키백과 또는 위키피디아는 누구나 자유롭게 쓸 수 있는 다언어판 ' '인터넷 백과사전이다.', '2001년 1월 15일 지미 웨일스와 래리 생어가 시작하였으며, 대표적인 ' '집단 지성의 사례로 평가받고 있다.', '위키백과는 자유 저작물을 보유하고 상업적인 광고가 없으며 주로 ' '기부금을 통해 지원을 받는 비영리 단체인 위키미디어 재단에 의해 ' '소유되고 지원을 받고 있다.', '2020년 기준으로 영어판 600만여 개, 한국어판 517,949개를 비롯하여 ' '300여 언어판을 합하면 4천만 개 이상의 글이 수록되어 꾸준히 ' '성장하고 있으며 앞으로 더 성장할 예정이다.', '위키백과의 저작권은 크리에이티브 커먼즈 라이선스(CCL)와 GNU 자유 ' '문서(GFDL)의 2중 라이선스를 따른다.', '두 라이선스 모두 자유 콘텐츠를 위한 것으로 일정한 요건을 갖추면 ' '사용에 제약을 받지 않는다.' ])
def test_english_sentence_splitting_in_multiprocessing(): _dummy_sentence_corpus = ( 'Wikipedia is a multilingual online encyclopedia created and ' 'maintained as an open collaboration project by a community of ' 'volunteer editors using a wiki-based editing system. It is the ' 'largest and most popular general reference work on the World Wide ' 'Web. It is also one of the 15 most popular websites ranked by Alexa, ' 'as of August 2020. It features exclusively free content and no ' 'commercial ads. It is hosted by the Wikimedia Foundation, a ' 'non-profit organization funded primarily through donations.') # Create worker process function to split sentences in multi-processing. def _process(text: str, splitter: SentenceSplitter, queue: Queue): queue.put(splitter.tokenize(text)) # Prepare processes and queue to communicate with them. queue = Queue() splitter = SentenceSplitter('en') workers = [ Process(target=_process, args=(_dummy_sentence_corpus, splitter, queue), daemon=True) for _ in range(10) ] # Start the processes. for w in workers: w.start() # Check if all processes split sentences correctly. for _ in range(10): assert (queue.get() == [ 'Wikipedia is a multilingual online encyclopedia created and ' 'maintained as an open collaboration project by a community ' 'of volunteer editors using a wiki-based editing system.', 'It is the largest and most popular general reference work on ' 'the World Wide Web.', 'It is also one of the 15 most popular websites ranked by ' 'Alexa, as of August 2020.', 'It features exclusively free content and no commercial ads.', 'It is hosted by the Wikimedia Foundation, a non-profit ' 'organization funded primarily through donations.' ])
def test_korean_sentence_splitting(): _dummy_sentence_corpus = ('위키백과 또는 위키피디아는 누구나 자유롭게 쓸 수 있는 다언어판 인터넷 ' '백과사전이다. 2001년 1월 15일 지미 웨일스와 래리 생어가 시작하였으며, ' '대표적인 집단 지성의 사례로 평가받고 있다. 위키백과는 자유 저작물을 ' '보유하고 상업적인 광고가 없으며 주로 기부금을 통해 지원을 받는 비영리 ' '단체인 위키미디어 재단에 의해 소유되고 지원을 받고 있다. 2020년 기준으로 ' '영어판 600만여 개, 한국어판 517,949개를 비롯하여 300여 언어판을 합하면 ' '4천만 개 이상의 글이 수록되어 꾸준히 성장하고 있으며 앞으로 더 성장할 ' '예정이다. 위키백과의 저작권은 크리에이티브 커먼즈 라이선스(CCL)와 GNU ' '자유 문서(GFDL)의 2중 라이선스를 따른다. 두 라이선스 모두 자유 콘텐츠를 ' '위한 것으로 일정한 요건을 갖추면 사용에 제약을 받지 않는다.') assert (SentenceSplitter('ko').tokenize(_dummy_sentence_corpus) == [ '위키백과 또는 위키피디아는 누구나 자유롭게 쓸 수 있는 다언어판 ' '인터넷 백과사전이다.', '2001년 1월 15일 지미 웨일스와 래리 생어가 시작하였으며, 대표적인 ' '집단 지성의 사례로 평가받고 있다.', '위키백과는 자유 저작물을 보유하고 상업적인 광고가 없으며 주로 ' '기부금을 통해 지원을 받는 비영리 단체인 위키미디어 재단에 의해 ' '소유되고 지원을 받고 있다.', '2020년 기준으로 영어판 600만여 개, 한국어판 517,949개를 비롯하여 ' '300여 언어판을 합하면 4천만 개 이상의 글이 수록되어 꾸준히 ' '성장하고 있으며 앞으로 더 성장할 예정이다.', '위키백과의 저작권은 크리에이티브 커먼즈 라이선스(CCL)와 GNU 자유 ' '문서(GFDL)의 2중 라이선스를 따른다.', '두 라이선스 모두 자유 콘텐츠를 위한 것으로 일정한 요건을 갖추면 ' '사용에 제약을 받지 않는다.' ])
def test_english_sentence_splitting(): _dummy_sentence_corpus = ( 'Wikipedia is a multilingual online encyclopedia created and ' 'maintained as an open collaboration project by a community of ' 'volunteer editors using a wiki-based editing system. It is the ' 'largest and most popular general reference work on the World Wide ' 'Web. It is also one of the 15 most popular websites ranked by Alexa, ' 'as of August 2020. It features exclusively free content and no ' 'commercial ads. It is hosted by the Wikimedia Foundation, a ' 'non-profit organization funded primarily through donations.') assert (SentenceSplitter('en').tokenize(_dummy_sentence_corpus) == [ 'Wikipedia is a multilingual online encyclopedia created and ' 'maintained as an open collaboration project by a community ' 'of volunteer editors using a wiki-based editing system.', 'It is the largest and most popular general reference work on ' 'the World Wide Web.', 'It is also one of the 15 most popular websites ranked by ' 'Alexa, as of August 2020.', 'It features exclusively free content and no commercial ads.', 'It is hosted by the Wikimedia Foundation, a non-profit ' 'organization funded primarily through donations.' ])
class ParseRawFile(Builder): """A builder for parsing raw-formatted corpus files. Args: parser: an implementation of raw-formatted corpus parser. lang: language code of the target corpus dataset. min_len: minimum length of each document. max_len: maximum length of each document. newline: newline token which is used for replacing the line-break characters. num_workers: number of worker processes which runs :meth:`parse <langumo.building.Parser.parse>` """ def __init__(self, parser: Parser, lang: str, min_len: int, max_len: int, newline: str = '[NEWLINE]', num_workers: int = 1): self.parser = parser self.min_len = min_len self.max_len = max_len self.newline = newline self.num_workers = num_workers self.splitter = SentenceSplitter(lang) def _parse_worker(self, from_queue: Queue, to_queue: Queue): while True: # Get raw-formatted document from main process. document = from_queue.get() if document is None: to_queue.put(None) break # Parse the document to the plain text. parsed = self.parser.parse(document) # Divide the document into sequences with required length. group_sentences = [] for paragraph in parsed.splitlines(): for sentence in self.splitter.tokenize(paragraph): group_sentences.append(sentence) if sum(len(s) for s in group_sentences) > self.max_len: to_queue.put(' '.join(group_sentences)) group_sentences.clear() # Use custom line-break token instead of `\n` which is used for # separating sequences. if group_sentences: group_sentences.append(self.newline) # Use the remainder in dataset if its length is suitable. if group_sentences and group_sentences[-1] == self.newline: group_sentences = group_sentences[:-1] text = ' '.join(group_sentences) if len(text) > self.min_len and len(text) < self.max_len: to_queue.put(text) def _collect_worker(self, parsed: AuxiliaryFile, to_queue: Queue): terminated = 0 with parsed.open('w') as fp: while terminated < self.num_workers: text = to_queue.get() if text is None: terminated += 1 continue text += '\n' if not text.endswith('\n') else '' fp.write(text) def build(self, afm: AuxiliaryFileManager, raw: AuxiliaryFile ) -> AuxiliaryFile: parsed = afm.create() self.parser.prepare(raw) # Create processes for parsing texts in parallel and a process for # collecting the parsed texts and saving to the auxiliary file. from_queue, to_queue = Queue(), Queue() parsers = [Process(target=self._parse_worker, args=(from_queue, to_queue), daemon=True) for _ in range(self.num_workers)] collector = Process(target=self._collect_worker, args=(parsed, to_queue), daemon=True) # Start the processes. print(colorful.render(f'<r>[*]</r> parse raw-formatted corpus file ' f'with <g>{self.parser.__class__.__name__}</g>')) for p in parsers: p.start() collector.start() # Feed the extracted raw-formatted document to each parser process. for document in self.parser.extract(raw): from_queue.put(document) for _ in range(self.num_workers): from_queue.put(None) # Wait for terminating the processes. for p in parsers: p.join() collector.join() return parsed
def _process(text: str, splitter: SentenceSplitter, queue: Queue): queue.put(splitter.tokenize(text))