programing

텍스트를 어떻게 문장으로 나눌 수 있습니까?

bestprogram 2023. 7. 16. 13:44

텍스트를 어떻게 문장으로 나눌 수 있습니까?

저는 텍스트 파일을 가지고 있습니다.저는 문장 목록을 받아야 합니다.

어떻게 구현할 수 있습니까?약어로 점을 사용하는 등 미묘한 점들이 많습니다.

내 예전 규칙적인 표현은 잘 작동하지 않습니다.

re.compile('(\. |^|!|\?)([A-Z][^;↑\.<>@\^&/\[\]]*(\.|!|\?) )',re.M)

Natural Language Toolkit(nltk.org )에는 필요한 기능이 있습니다.이 그룹 게시는 다음과 같은 작업을 수행함을 나타냅니다.

import nltk.data

tokenizer = nltk.data.load('tokenizers/punkt/english.pickle')
fp = open("test.txt")
data = fp.read()
print '\n-----\n'.join(tokenizer.tokenize(data))

(안 먹어봤어요!)

이 기능은 Huckleberry Finn의 전체 텍스트를 약 0.1초 만에 문장으로 분할할 수 있으며 문장 구문 분석을 사소한 것으로 만드는 많은 고통스러운 에지 사례를 처리합니다. 를 들어, "John Johnson Jr.는 미국에서 태어났지만 엔지니어로 나이키에 입사하기 전에 이스라엘에서 박사 학위를 취득했습니다. 그는 또한 craigslist.org 에서 비즈니스 분석가로 일했습니다."

# -*- coding: utf-8 -*-
import re
alphabets= "([A-Za-z])"
prefixes = "(Mr|St|Mrs|Ms|Dr)[.]"
suffixes = "(Inc|Ltd|Jr|Sr|Co)"
starters = "(Mr|Mrs|Ms|Dr|Prof|Capt|Cpt|Lt|He\s|She\s|It\s|They\s|Their\s|Our\s|We\s|But\s|However\s|That\s|This\s|Wherever)"
acronyms = "([A-Z][.][A-Z][.](?:[A-Z][.])?)"
websites = "[.](com|net|org|io|gov|edu|me)"
digits = "([0-9])"
multiple_dots = r'\.{2,}'

def split_into_sentences(text: str) -> list[str]:
    """
    Split the text into sentences.

    If the text contains substrings "<prd>" or "<stop>", they would lead 
    to incorrect splitting because they are used as markers for splitting.

    :param text: text to be split into sentences
    :type text: str

    :return: list of sentences
    :rtype: list[str]
    """
    text = " " + text + "  "
    text = text.replace("\n"," ")
    text = re.sub(prefixes,"\\1<prd>",text)
    text = re.sub(websites,"<prd>\\1",text)
    text = re.sub(digits + "[.]" + digits,"\\1<prd>\\2",text)
    text = re.sub(multiple_dots, lambda match: "<prd>" * len(match.group(0)) + "<stop>", text)
    if "Ph.D" in text: text = text.replace("Ph.D.","Ph<prd>D<prd>")
    text = re.sub("\s" + alphabets + "[.] "," \\1<prd> ",text)
    text = re.sub(acronyms+" "+starters,"\\1<stop> \\2",text)
    text = re.sub(alphabets + "[.]" + alphabets + "[.]" + alphabets + "[.]","\\1<prd>\\2<prd>\\3<prd>",text)
    text = re.sub(alphabets + "[.]" + alphabets + "[.]","\\1<prd>\\2<prd>",text)
    text = re.sub(" "+suffixes+"[.] "+starters," \\1<stop> \\2",text)
    text = re.sub(" "+suffixes+"[.]"," \\1<prd>",text)
    text = re.sub(" " + alphabets + "[.]"," \\1<prd>",text)
    if "”" in text: text = text.replace(".”","”.")
    if "\"" in text: text = text.replace(".\"","\".")
    if "!" in text: text = text.replace("!\"","\"!")
    if "?" in text: text = text.replace("?\"","\"?")
    text = text.replace(".",".<stop>")
    text = text.replace("?","?<stop>")
    text = text.replace("!","!<stop>")
    text = text.replace("<prd>",".")
    sentences = text.split("<stop>")
    sentences = [s.strip() for s in sentences]
    if sentences and not sentences[-1]: sentences = sentences[:-1]
    return sentences

와의 nltk:

>>> from nltk.tokenize import sent_tokenize

1 예 1: 예 1: split_into_sentences여기가 더 낫습니다(많은 사례를 명시적으로 다루고 있기 때문에).

>>> text = 'Some sentence. Mr. Holmes...This is a new sentence!And This is another one.. Hi '

>>> split_into_sentences(text)
['Some sentence.',
 'Mr. Holmes...',
 'This is a new sentence!',
 'And This is another one..',
 'Hi']

>>> sent_tokenize(text)
['Some sentence.',
 'Mr.',
 'Holmes...This is a new sentence!And This is another one.. Hi']

2 예 2: 예 2: nltk.tokenize.sent_tokenizeML 모델을 사용하기 때문에 여기가 더 좋습니다.

>>> text = 'The U.S. Drug Enforcement Administration (DEA) says hello. And have a nice day.'

>>> split_into_sentences(text)
['The U.S.',
 'Drug Enforcement Administration (DEA) says hello.',
 'And have a nice day.']

>>> sent_tokenize(text)
['The U.S. Drug Enforcement Administration (DEA) says hello.',
 'And have a nice day.']

텍스트를 문장으로 분할하기 위해 정규식을 사용하는 대신 nltk 라이브러리를 사용할 수도 있습니다.

>>> from nltk import tokenize
>>> p = "Good morning Dr. Adams. The patient is waiting for you in room number 3."

>>> tokenize.sent_tokenize(p)
['Good morning Dr. Adams.', 'The patient is waiting for you in room number 3.']

참조: https://stackoverflow.com/a/9474645/2877052

정규식 대신 Spacy를 사용해 볼 수 있습니다.저는 그것을 사용하고 그것은 그 일을 합니다.

import spacy
nlp = spacy.load('en')

text = '''Your text here'''
tokens = nlp(text)

for sent in tokens.sents:
    print(sent.string.strip())

여기 외부 라이브러리에 의존하지 않는 도로 접근 방식의 중간이 있습니다.저는 목록 이해를 사용하여 약어와 종결자 사이의 중복을 제외하고 종결자에 대한 변형 간의 중복을 제외합니다. 예를 들어 '. vs.'.

abbreviations = {'dr.': 'doctor', 'mr.': 'mister', 'bro.': 'brother', 'bro': 'brother', 'mrs.': 'mistress', 'ms.': 'miss', 'jr.': 'junior', 'sr.': 'senior',
                 'i.e.': 'for example', 'e.g.': 'for example', 'vs.': 'versus'}
terminators = ['.', '!', '?']
wrappers = ['"', "'", ')', ']', '}']


def find_sentences(paragraph):
   end = True
   sentences = []
   while end > -1:
       end = find_sentence_end(paragraph)
       if end > -1:
           sentences.append(paragraph[end:].strip())
           paragraph = paragraph[:end]
   sentences.append(paragraph)
   sentences.reverse()
   return sentences


def find_sentence_end(paragraph):
    [possible_endings, contraction_locations] = [[], []]
    contractions = abbreviations.keys()
    sentence_terminators = terminators + [terminator + wrapper for wrapper in wrappers for terminator in terminators]
    for sentence_terminator in sentence_terminators:
        t_indices = list(find_all(paragraph, sentence_terminator))
        possible_endings.extend(([] if not len(t_indices) else [[i, len(sentence_terminator)] for i in t_indices]))
    for contraction in contractions:
        c_indices = list(find_all(paragraph, contraction))
        contraction_locations.extend(([] if not len(c_indices) else [i + len(contraction) for i in c_indices]))
    possible_endings = [pe for pe in possible_endings if pe[0] + pe[1] not in contraction_locations]
    if len(paragraph) in [pe[0] + pe[1] for pe in possible_endings]:
        max_end_start = max([pe[0] for pe in possible_endings])
        possible_endings = [pe for pe in possible_endings if pe[0] != max_end_start]
    possible_endings = [pe[0] + pe[1] for pe in possible_endings if sum(pe) > len(paragraph) or (sum(pe) < len(paragraph) and paragraph[sum(pe)] == ' ')]
    end = (-1 if not len(possible_endings) else max(possible_endings))
    return end


def find_all(a_str, sub):
    start = 0
    while True:
        start = a_str.find(sub, start)
        if start == -1:
            return
        yield start
        start += len(sub)

이 항목에서 Karl의 find_all 함수를 사용했습니다: Python에서 하위 문자열의 모든 발생 항목 찾기

NLTK에서 문장 토큰화 기능을 사용할 수도 있습니다.

from nltk.tokenize import sent_tokenize
sentence = "As the most quoted English writer Shakespeare has more than his share of famous quotes.  Some Shakespare famous quotes are known for their beauty, some for their everyday truths and some for their wisdom. We often talk about Shakespeare’s quotes as things the wise Bard is saying to us but, we should remember that some of his wisest words are spoken by his biggest fools. For example, both ‘neither a borrower nor a lender be,’ and ‘to thine own self be true’ are from the foolish, garrulous and quite disreputable Polonius in Hamlet."

sent_tokenize(sentence)

저는 spacCy를 죽을 때까지 사랑하지만, 최근에 문장 토큰화를 위한 두 가지 새로운 접근법을 발견했습니다.하나는 마이크로소프트의 BlingFire(놀랍게 빠른 속도)이고, 다른 하나는 AI2의 PySBD(매우 정확함)입니다.

text = ...

from blingfire import text_to_sentences
sents = text_to_sentences(text).split('\n')

from pysbd import Segmenter
segmenter = Segmenter(language='en', clean=False)
sents = segmenter.segment(text)

저는 5가지 다른 방법으로 20,000개의 문장을 분리했습니다.AMD 스레드리퍼 리눅스 시스템의 경과 시간은 다음과 같습니다.

  • spaCy 센텐시저: 1.16934s
  • spaCy 구문 분석: 25.97063s
  • PySBD: 9.03505s
  • NLTK: 0.30512s
  • BlingFire: 0.07933s

업데이트: BlingFire를 전체 소문자 텍스트에 사용하려고 시도했지만 비참하게 실패했습니다.당분간 프로젝트에 PySBD를 사용할 예정입니다.

단순한 경우(문장이 정상적으로 종료되는 경우)에는 다음과 같이 적용됩니다.

import re
text = ''.join(open('somefile.txt').readlines())
sentences = re.split(r' *[\.\?!][\'"\)\]]* *', text)

은 정식은입니다.*\. +0 이상의 공백으로 둘러싸인 마침표를 왼쪽으로, 1 이상의 공백으로 둘러싸인 마침표를 오른쪽으로 일치시킵니다(예: re.context에서 마침표가 문장의 변화로 계산되는 것을 방지하기 위해).

가장 강력한 솔루션은 아니지만 대부분의 경우에는 문제가 없습니다.이것이 다루지 않는 유일한 경우는 약어입니다(아마도 문장 목록을 살펴보고 각 문자열을 확인합니다).sentences대문자로 시작합니까?)

공간 사용:

import spacy

nlp = spacy.load('en_core_web_sm')
text = "How are you today? I hope you have a great day"
tokens = nlp(text)
for sent in tokens.sents:
    print(sent.string.strip())

이것은 n개의 문장으로 분할된 첫 번째 게시물이기 때문에 이것을 넣는 것이 나을 수 있습니다.

이것은 가변 분할 길이와 함께 작동하는데, 이것은 마지막에 함께 결합되는 문장을 나타냅니다.

import nltk
//nltk.download('punkt')
from more_itertools import windowed

split_length = 3 // 3 sentences for example 

elements = nltk.tokenize.sent_tokenize(text)
segments = windowed(elements, n=split_length, step=split_length)
text_splits = []
for seg in segments:
          txt = " ".join([t for t in seg if t])
          if len(txt) > 0:
                text_splits.append(txt)

NLTK의 sent_tokenize가 문제가 되지 않고(예: 긴 텍스트에 많은 GPU RAM이 필요함) 정규식이 언어 간에 제대로 작동하지 않으면 문장 분할기가 시도해 볼 가치가 있을 수 있습니다.

스탠자 사용은 많은 인간 언어에 사용할 수 있는 자연어 처리 라이브러리입니다.

import stanza

stanza.download('en')
nlp = stanza.Pipeline(lang='en', processors='tokenize')

doc = nlp(t_en)
for sentence in doc.sentences:
    print(sentence.text)

NLTK가 그 목적에 가장 적합하다는 것은 의심의 여지가 없습니다.그러나 NLTK를 시작하는 것은 상당히 고통스럽습니다(그러나 일단 설치하면 보상을 받을 뿐입니다).

http://pythonicprose.blogspot.com/2009/09/python-split-paragraph-into-sentences.html 에서 사용할 수 있는 간단한 리베이스 코드는 다음과 같습니다.

# split up a paragraph into sentences
# using regular expressions


def splitParagraphIntoSentences(paragraph):
    ''' break a paragraph into sentences
        and return a list '''
    import re
    # to split by multile characters

    #   regular expressions are easiest (and fastest)
    sentenceEnders = re.compile('[.!?]')
    sentenceList = sentenceEnders.split(paragraph)
    return sentenceList


if __name__ == '__main__':
    p = """This is a sentence.  This is an excited sentence! And do you think this is a question?"""

    sentences = splitParagraphIntoSentences(p)
    for s in sentences:
        print s.strip()

#output:
#   This is a sentence
#   This is an excited sentence

#   And do you think this is a question 

이것이 라틴어, 중국어, 영어 텍스트에 도움이 되기를 바랍니다.

import re

punctuation = re.compile(r"([^\d+])(\.|!|\?|;|\n|。|!|?|;|…| |!|؟|؛)+")
lines = []

with open('myData.txt','r',encoding="utf-8") as myFile:
    lines = punctuation.sub(r"\1\2<pad>", myFile.read())
    lines = [line.strip() for line in lines.split("<pad>") if line.strip()]

비슷한 작업을 하다가 몇 개의 링크를 따르고 몇 개의 연습을 nltk에 대해 작업함으로써 이 질문을 발견했습니다. 아래 코드는 마법처럼 저에게 효과가 있었습니다.

from nltk.tokenize import sent_tokenize 
  
text = "Hello everyone. Welcome to GeeksforGeeks. You are studying NLP article"
sent_tokenize(text) 

출력:

['Hello everyone.',
 'Welcome to GeeksforGeeks.',
 'You are studying NLP article']

출처: https://www.geeksforgeeks.org/nlp-how-tokenizing-text-sentence-words-works/

또한 위의 답변 중 일부에 포함되지 않은 추가적인 최상위 도메인도 주의해야 합니다.

예를 들어 .info, .biz, .ru, .online은 문장 파서를 몇 개 던지지만 위에는 포함되지 않습니다.

다음은 최상위 도메인의 빈도에 대한 몇 가지 정보입니다. https://www.westhost.com/blog/the-most-popular-top-level-domains-in-2017/

위의 코드를 다음과 같이 편집하여 이 문제를 해결할 수 있습니다.

alphabets= "([A-Za-z])"
prefixes = "(Mr|St|Mrs|Ms|Dr)[.]"
suffixes = "(Inc|Ltd|Jr|Sr|Co)"
starters = "(Mr|Mrs|Ms|Dr|He\s|She\s|It\s|They\s|Their\s|Our\s|We\s|But\s|However\s|That\s|This\s|Wherever)"
acronyms = "([A-Z][.][A-Z][.](?:[A-Z][.])?)"
websites = "[.](com|net|org|io|gov|ai|edu|co.uk|ru|info|biz|online)"

다음 기능을 사용하여 러시아어(및 일부 다른 언어)에 대한 새 토큰라이저를 만들 수 있습니다.

def russianTokenizer(text):
    result = text
    result = result.replace('.', ' . ')
    result = result.replace(' .  .  . ', ' ... ')
    result = result.replace(',', ' , ')
    result = result.replace(':', ' : ')
    result = result.replace(';', ' ; ')
    result = result.replace('!', ' ! ')
    result = result.replace('?', ' ? ')
    result = result.replace('\"', ' \" ')
    result = result.replace('\'', ' \' ')
    result = result.replace('(', ' ( ')
    result = result.replace(')', ' ) ') 
    result = result.replace('  ', ' ')
    result = result.replace('  ', ' ')
    result = result.replace('  ', ' ')
    result = result.replace('  ', ' ')
    result = result.strip()
    result = result.split(' ')
    return result

그런 다음 이렇게 부릅니다.

text = 'вы выполняете поиск, используя Google SSL;'
tokens = russianTokenizer(text)

저는 자막 파일을 읽고 문장으로 나누어야 했습니다.사전 처리 후(예: .srt 파일에서 시간 정보 제거 등) 변수 fullFile은 부제 파일의 전체 텍스트를 포함했습니다.아래의 조잡한 방법은 그것들을 깔끔하게 문장으로 나눕니다.아마도 저는 문장이 항상 공백으로 끝나는 것이 행운이었을 것입니다.이것을 먼저 시도하고 예외가 있는 경우 확인 및 균형을 추가합니다.

# Very approximate way to split the text into sentences - Break after ? . and !
fullFile = re.sub("(\!|\?|\.) ","\\1<BRK>",fullFile)
sentences = fullFile.split("<BRK>");
sentFile = open("./sentences.out", "w+");
for line in sentences:
    sentFile.write (line);
    sentFile.write ("\n");
sentFile.close;

오! 그럼.저는 이제 제 콘텐츠가 스페인어였기 때문에 "미스터 스미스" 등을 다루는 문제가 없었다는 것을 깨달았습니다.그래도 누군가 빠르고 더러운 파서를 원한다면,

띄엄띄엄

import spacy
nlp = spacy.load('en_core_web_sm')
doc = nlp(u'This is first.This is second.This is Thired ')
for sentence in doc.sents:
  print(sentence)

하지만 색인으로 문장을 얻고 싶다면 예:

#don't work
 doc.sents[0]

사용하다

list( doc.sents)[0]

Spacy v3.5 사용:

import spacy

nlp_sentencizer = spacy.blank("en")
nlp_sentencizer.add_pipe("sentencizer")

text = "How are you today? I hope you have a great day"
tokens = nlp_sentencizer(text)
[str(sent) for sent in tokens.sents]

언급URL : https://stackoverflow.com/questions/4576077/how-can-i-split-a-text-into-sentences