단어에서 음절 감지


138

단어로 음절을 감지하는 상당히 효율적인 방법을 찾아야합니다. 예 :

보이지 않는-> in-vi-sib-le

사용할 수있는 일부 음절 규칙이 있습니다.

V CV VC CVC CCV CCCV CVCC

* 여기서 V는 모음이고 C는 자음입니다. 예 :

발음 (5 개 발음); CV-CVC-CV-V-CVC)

나는 정규식 (음절을 세고 싶을 때만 도움이 됨) 또는 하드 코딩 된 규칙 정의 (매우 비효율적 인 것으로 판명 된 무력 접근법)를 사용하고 마침내 유한 상태 오토 마타를 사용하는 몇 가지 방법을 시도했습니다. 유용한 결과는 없습니다).

내 응용 프로그램의 목적은 주어진 언어로 모든 음절의 사전을 만드는 것입니다. 이 사전은 나중에 맞춤법 검사 응용 프로그램 (베이지 분류기를 사용) 및 텍스트-음성 합성에 사용됩니다.

이전 접근법 외에도이 문제를 해결할 수있는 다른 방법에 대한 팁을 줄 수 있다면 감사하겠습니다.

Java로 작업하지만 C / C ++, C #, Python, Perl의 팁은 저에게 효과적입니다.


실제로 실제 나눗셈 포인트 또는 단어의 음절 수를 원하십니까? 후자의 경우, 텍스트 음성 변환 사전에서 단어를 찾고 모음 소리를 인코딩하는 음소를 계산하십시오.
— Adrian McCarthy

가장 효율적인 방법 (계산 방식; 스토리지 방식이 아닌)은 단어로 키를 사용하고 음절 수를 값으로 사용하여 Python 사전을 갖는 것입니다. 그러나 여전히 사전에 포함되지 않은 단어에 대해서는 대체가 필요합니다. 그런 사전을 찾으면 알려주십시오!
— Brōtsyorfuzthrāx

답변:


120

하이픈 넣기 목적으로이 문제에 대한 TeX 접근 방식에 대해 읽으십시오. 특히 Frank Liang의 논문 논문 Hy-phen-a-tion by Comp-put-er 참조 . 그의 알고리즘은 매우 정확하며 알고리즘이 작동하지 않는 경우에 대한 작은 예외 사전을 포함합니다.


52
나는 당신이 주제에 논문 논문을 인용 한 것을 좋아합니다. 이것은 쉬운 질문이 아닐 수도 있다는 원래 포스터에 대한 작은 힌트입니다.
— Karl

그렇습니다. 나는 많은 질문을하지는 않았지만 간단한 질문은 아니라는 것을 알고 있습니다. 그래도 문제를 과소 평가했지만 앱의 다른 부분에서 작업하고 나중에이 '간단한'문제로 돌아갈 것이라고 생각했습니다. Silly me :)
— user50705

나는 논문을 읽고 매우 도움이되었다는 것을 알았습니다. 이 접근 방식의 문제점은 알바니아 언어에 대한 패턴이 없지만 패턴을 생성 할 수있는 도구를 찾았다는 것입니다. 어쨌든, 내 목적을 위해 규칙 기반 앱을 작성하여 문제를 해결했습니다.
— user50705

10
TeX 알고리즘은 음절과 정확히 일치하지 않는 합법적 인 하이픈 포인트를 찾기위한 것입니다. 하이픈 넣기 지점이 음절 구분에 해당하는 것이 사실이지만 모든 음절 구분이 올바른 하이픈 점은 아닙니다. 예를 들어, 하이픈은 (보통) 글자 나 단어의 양쪽 끝에서 사용되지 않습니다. 또한 TeX 패턴은 가양성에 대한가 음성을 차단하도록 조정되었다고 생각합니다 (합법적 인 하이픈 기회가 누락 된 경우에도 하이픈을 속하지 않는 곳에 절대 하이픈을 넣지 마십시오).
— Adrian McCarthy

1
나는 하이픈이 답이라고 믿지 않습니다.
— Ezequiel

46

나는이 페이지를 우연히 찾아서 같은 것을 찾고, Liang 논문의 몇 가지 구현을 여기에서 발견했다 : https://github.com/mnater/hyphenator 또는 후속 : https://github.com/mnater/Hyphenopoly

고유하지 않은 문제에 대해 자유롭게 사용할 수있는 코드를 적용하는 대신 60 페이지 논문을 읽는 것을 좋아하지 않는 한 그렇지 않습니다. :)


동의 - 훨씬 더 편리 그냥 기존 implmentation 사용
— hoju

41

NLTK를 사용하는 솔루션은 다음과 같습니다 .

from nltk.corpus import cmudict
d = cmudict.dict()
def nsyl(word):
  return [len(list(y for y in x if y[-1].isdigit())) for x in d[word.lower()]] 

이봐, 작은 아기 오류 감사합니다 함수 def nsyl (word) : return [len (list (y (-1의 경우 y [-1] .isdigit ())의 경우 x의 d [word.lower ()]) ]
— Gourneau

6
그 말에없는 단어의 대체물로 무엇을 제안 하시겠습니까?
— Dan Gayle

4
@Pureferret cmudict 는 북미 영어 단어를 발음하는 사전입니다. 음절보다 짧은 음소로 단어를 나눕니다 (예 : 'cat'이라는 단어는 K-AE-T의 세 음소로 나뉩니다). 그러나 모음에는 단어의 발음에 따라 0, 1 또는 2의 "스트레스 마커"도 있습니다 (따라서 'cat'의 AE는 AE1이됩니다). 답변의 코드는 스트레스 마커와 모음 수를 계산하여 음절 수를 효과적으로 제공합니다 (OP의 예에서 각 음절에 정확히 하나의 모음이있는 방법에 유의하십시오).
— billy_chapters

1
이것은 음절이 아닌 음절의 수를 반환합니다.
— Adam Michael Wood

19

텍스트 블록의 flesch-kincaid 및 flesch reading score를 계산하는 프로그램 에서이 문제를 해결하려고합니다. 내 알고리즘은이 웹 사이트에서 찾은 것 ( http://www.howmanysyllables.com/howtocountsyllables.html)을 사용 하며 합리적으로 가깝습니다. 보이지 않는 하이픈과 같은 복잡한 단어에는 여전히 문제가 있지만 내 목표를 위해 야구장에 도착한다는 것을 알았습니다.

구현하기 쉽다는 단점이 있습니다. 나는 "es"가 음절인지 아닌지를 발견했다. 도박이지만 알고리즘에서 es를 제거하기로 결정했습니다.

private int CountSyllables(string word)
    {
        char[] vowels = { 'a', 'e', 'i', 'o', 'u', 'y' };
        string currentWord = word;
        int numVowels = 0;
        bool lastWasVowel = false;
        foreach (char wc in currentWord)
        {
            bool foundVowel = false;
            foreach (char v in vowels)
            {
                //don't count diphthongs
                if (v == wc && lastWasVowel)
                {
                    foundVowel = true;
                    lastWasVowel = true;
                    break;
                }
                else if (v == wc && !lastWasVowel)
                {
                    numVowels++;
                    foundVowel = true;
                    lastWasVowel = true;
                    break;
                }
            }

            //if full cycle and no vowel found, set lastWasVowel to false;
            if (!foundVowel)
                lastWasVowel = false;
        }
        //remove es, it's _usually? silent
        if (currentWord.Length > 2 && 
            currentWord.Substring(currentWord.Length - 2) == "es")
            numVowels--;
        // remove silent e
        else if (currentWord.Length > 1 &&
            currentWord.Substring(currentWord.Length - 1) == "e")
            numVowels--;

        return numVowels;
    }

적절한 이름으로 음절을 찾는 간단한 시나리오의 경우 처음에는 충분히 잘 작동하는 것 같습니다. 그것을 넣어 주셔서 감사합니다.
— Norman H

7

LaTeX 하이픈 넣기 알고리즘으로 완전히 해결되지 않는 특히 어려운 문제입니다. 사용 가능한 몇 가지 방법과 문제에 대한 요약은 영어 자동 실 라벨 알고리즘 평가 (Marchand, Adsett, Damper 2007)에서 확인할 수 있습니다.


5

왜 계산합니까? 모든 온라인 사전에는이 정보가 있습니다. http://dictionary.reference.com/browse/invisible in · vis · i · ble


3
이름과 같이 사전에 나타나지 않는 단어에 대해서는 효과가있을 수 있습니다.
— Wouter Lievens

4
@WouterLievens : 이름이 자동 음절 구문 분석을 위해 충분히 동작하는 곳은 없다고 생각합니다. 영어 이름에 대한 음절 파서는 인도어와 나이지리아 출신의 이름은 물론 웨일스 어 또는 스코틀랜드 출신의 이름에서 비참하게 실패하지만 런던과 같은 어딘가의 단일 방 에서이 모든 것을 찾을 수 있습니다.
— Jean-François Corbett

이것은 스케치 영역에 대한 순전 한 휴리스틱 접근법을 고려할 때 인간이 제공 할 수있는 것보다 더 나은 성능을 기대하는 것이 합리적이지 않다는 점을 명심해야합니다.
— 대런 링거

5

C #에서 빠르고 더러운 구현을 공유해 주신 Joe Basirico에게 감사드립니다. 나는 큰 라이브러리를 사용했지만 작동하지만 일반적으로 약간 느리고 빠른 프로젝트의 경우 방법이 잘 작동합니다.

다음은 테스트 사례와 함께 Java 코드입니다.

public static int countSyllables(String word)
{
    char[] vowels = { 'a', 'e', 'i', 'o', 'u', 'y' };
    char[] currentWord = word.toCharArray();
    int numVowels = 0;
    boolean lastWasVowel = false;
    for (char wc : currentWord) {
        boolean foundVowel = false;
        for (char v : vowels)
        {
            //don't count diphthongs
            if ((v == wc) && lastWasVowel)
            {
                foundVowel = true;
                lastWasVowel = true;
                break;
            }
            else if (v == wc && !lastWasVowel)
            {
                numVowels++;
                foundVowel = true;
                lastWasVowel = true;
                break;
            }
        }
        // If full cycle and no vowel found, set lastWasVowel to false;
        if (!foundVowel)
            lastWasVowel = false;
    }
    // Remove es, it's _usually? silent
    if (word.length() > 2 && 
            word.substring(word.length() - 2) == "es")
        numVowels--;
    // remove silent e
    else if (word.length() > 1 &&
            word.substring(word.length() - 1) == "e")
        numVowels--;
    return numVowels;
}

public static void main(String[] args) {
    String txt = "what";
    System.out.println("txt="+txt+" countSyllables="+countSyllables(txt));
    txt = "super";
    System.out.println("txt="+txt+" countSyllables="+countSyllables(txt));
    txt = "Maryland";
    System.out.println("txt="+txt+" countSyllables="+countSyllables(txt));
    txt = "American";
    System.out.println("txt="+txt+" countSyllables="+countSyllables(txt));
    txt = "disenfranchized";
    System.out.println("txt="+txt+" countSyllables="+countSyllables(txt));
    txt = "Sophia";
    System.out.println("txt="+txt+" countSyllables="+countSyllables(txt));
}

결과는 예상대로였습니다 (Flesch-Kincaid에게는 충분하게 작동합니다).

txt=what countSyllables=1
txt=super countSyllables=2
txt=Maryland countSyllables=3
txt=American countSyllables=3
txt=disenfranchized countSyllables=5
txt=Sophia countSyllables=2

5

@Tihamer와 @ joe-basirico를 범핑. 매우 유용한 기능으로 완벽 하지는 않지만 대부분의 중소 프로젝트에 적합합니다. Joe, 나는 당신의 코드의 구현을 파이썬으로 다시 작성했습니다 :

def countSyllables(word):
    vowels = "aeiouy"
    numVowels = 0
    lastWasVowel = False
    for wc in word:
        foundVowel = False
        for v in vowels:
            if v == wc:
                if not lastWasVowel: numVowels+=1   #don't count diphthongs
                foundVowel = lastWasVowel = True
                        break
        if not foundVowel:  #If full cycle and no vowel found, set lastWasVowel to false
            lastWasVowel = False
    if len(word) > 2 and word[-2:] == "es": #Remove es - it's "usually" silent (?)
        numVowels-=1
    elif len(word) > 1 and word[-1:] == "e":    #remove silent e
        numVowels-=1
    return numVowels

누군가가 이것을 유용하게 사용하기를 바랍니다!


4

Perl에는 Lingua :: Phonology :: Syllable 모듈이 있습니다. 시도해 보거나 알고리즘을 살펴보십시오. 나는 거기에 몇 가지 다른 오래된 모듈도 보았습니다.

정규식이 왜 음절 만 제공하는지 이해하지 못합니다. 캡처 괄호를 사용하여 음절 자체를 얻을 수 있어야합니다. 작동하는 정규 표현식을 구성 할 수 있다고 가정하십시오.


4

오늘 저는 Frank Liang의 하이픈 넣기 알고리즘을 영어 또는 독일어 패턴으로 구현 한 Java 구현을 발견 했습니다 .

동굴 : .tex패턴 파일 의 마지막 줄을 제거하는 것이 중요합니다. 그렇지 않으면 해당 파일을 Maven Central의 현재 버전으로로드 할 수 없기 때문입니다.

를로드하고 사용하려면 hyphenator다음 Java 코드 스 니펫을 사용할 수 있습니다. 필요한 패턴을 포함하는 파일 texTable이름입니다 .tex. 이러한 파일은 프로젝트 github 사이트에서 사용할 수 있습니다.

 private Hyphenator createHyphenator(String texTable) {
        Hyphenator hyphenator = new Hyphenator();
        hyphenator.setErrorHandler(new ErrorHandler() {
            public void debug(String guard, String s) {
                logger.debug("{},{}", guard, s);
            }

            public void info(String s) {
                logger.info(s);
            }

            public void warning(String s) {
                logger.warn("WARNING: " + s);
            }

            public void error(String s) {
                logger.error("ERROR: " + s);
            }

            public void exception(String s, Exception e) {
                logger.error("EXCEPTION: " + s, e);
            }

            public boolean isDebugged(String guard) {
                return false;
            }
        });

        BufferedReader table = null;

        try {
            table = new BufferedReader(new InputStreamReader(Thread.currentThread().getContextClassLoader()
                    .getResourceAsStream((texTable)), Charset.forName("UTF-8")));
            hyphenator.loadTable(table);
        } catch (Utf8TexParser.TexParserException e) {
            logger.error("error loading hyphenation table: {}", e.getLocalizedMessage(), e);
            throw new RuntimeException("Failed to load hyphenation table", e);
        } finally {
            if (table != null) {
                try {
                    table.close();
                } catch (IOException e) {
                    logger.error("Closing hyphenation table failed", e);
                }
            }
        }

        return hyphenator;
    }

이후 Hyphenator에 사용할 준비가되었습니다. 음절을 탐지하기 위해 기본 개념은 제공된 하이픈으로 용어를 분리하는 것입니다.

    String hyphenedTerm = hyphenator.hyphenate(term);

    String hyphens[] = hyphenedTerm.split("\u00AD");

    int syllables = hyphens.length;

"\u00ADAPI는 normal을 반환하지 않기 때문에 " 로 분할해야합니다 "-".

이 방법은 다양한 언어를 지원하고 독일어 하이픈을 더 정확하게 감지하므로 Joe Basirico의 답변보다 성능이 우수합니다.


4

나는 조금 전에이 똑같은 문제에 부딪쳤다.

대부분의 단어를 빠르고 정확하게 조회하기 위해 CMU 발음 사전 을 사용했습니다 . 사전에없는 단어의 경우 음절 수를 예측할 때 ~ 98 % 정확한 기계 학습 모델로 돌아 왔습니다.

사용하기 쉬운 파이썬 모듈로 모든 것을 마무리했습니다 : https://github.com/repp/big-phoney

설치: pip install big-phoney

음절 수 :

from big_phoney import BigPhoney
phoney = BigPhoney()
phoney.count_syllables('triceratops')  # --> 4

파이썬을 사용하지 않고 ML 모델 기반 접근 방식을 사용 하려면 음절 계산 모델이 Kaggle에서 어떻게 작동하는지에 대해 자세히 작성했습니다 .


이것은 정말 멋지다. iOS에서 사용하기 위해 결과 Keras 모델을 CoreML 모델로 변환 한 사람이 있습니까?
— Alexsander Akers 2016 년

2

@ joe-basirico와 @tihamer에게 감사합니다. @tihamer의 코드를 Lua 5.1, 5.2 및 luajit 2로 이식했습니다 ( 대부분 다른 버전의 lua에서도 실행될 것입니다 ).

countsyllables.lua

function CountSyllables(word)
  local vowels = { 'a','e','i','o','u','y' }
  local numVowels = 0
  local lastWasVowel = false

  for i = 1, #word do
    local wc = string.sub(word,i,i)
    local foundVowel = false;
    for _,v in pairs(vowels) do
      if (v == string.lower(wc) and lastWasVowel) then
        foundVowel = true
        lastWasVowel = true
      elseif (v == string.lower(wc) and not lastWasVowel) then
        numVowels = numVowels + 1
        foundVowel = true
        lastWasVowel = true
      end
    end

    if not foundVowel then
      lastWasVowel = false
    end
  end

  if string.len(word) > 2 and
    string.sub(word,string.len(word) - 1) == "es" then
    numVowels = numVowels - 1
  elseif string.len(word) > 1 and
    string.sub(word,string.len(word)) == "e" then
    numVowels = numVowels - 1
  end

  return numVowels
end

그리고 재미있는 테스트가 작동하는지 확인합니다 ( 예상대로 ).

countsyllables.tests.lua

require "countsyllables"

tests = {
  { word = "what", syll = 1 },
  { word = "super", syll = 2 },
  { word = "Maryland", syll = 3},
  { word = "American", syll = 4},
  { word = "disenfranchized", syll = 5},
  { word = "Sophia", syll = 2},
  { word = "End", syll = 1},
  { word = "I", syll = 1},
  { word = "release", syll = 2},
  { word = "same", syll = 1},
}

for _,test in pairs(tests) do
  local resultSyll = CountSyllables(test.word)
  assert(resultSyll == test.syll,
    "Word: "..test.word.."\n"..
    "Expected: "..test.syll.."\n"..
    "Result: "..resultSyll)
end

print("Tests passed.")

"End"와 "I"테스트 케이스를 두 개 더 추가했습니다. 수정은 대소 문자를 구분하지 않고 문자열을 비교하는 것이 었습니다. 동일한 문제로 고통 받고 기능을 업데이트하려는 경우 @ joe-basirico와 tihamer를 Ping'ing합니다.
— josefnpat

@tihamer American은 4 음절입니다!
— josefnpat

2

음절을 셀 수있는 적절한 방법을 찾지 못해 방법을 직접 설계했습니다.

내 방법은 https://stackoverflow.com/a/32784041/2734752 에서 볼 수 있습니다.

사전과 알고리즘 방법의 조합을 사용하여 음절을 계산합니다.

내 라이브러리를 볼 수 있습니다 : https://github.com/troywatson/Lawrence-Style-Checker

방금 알고리즘을 테스트했으며 99.4 %의 공격률을 나타 냈습니다!

Lawrence lawrence = new Lawrence();

System.out.println(lawrence.getSyllable("hyphenation"));
System.out.println(lawrence.getSyllable("computer"));

산출:

4
3


구문 강조를 참조하십시오 . SO 편집기에 도움말 버튼 (물음표)이있어 링크 된 페이지로 이동합니다.
— IKavanagh

0

많은 테스트를 수행하고 하이픈 넣기 패키지를 시험해 본 후에 여러 예제를 기반으로 직접 작성했습니다. 또한 하이픈 딕셔너리 사전과 인터페이스 하는 pyhyphen및 pyphen패키지를 시도했지만 많은 경우 음절 수가 잘못되었습니다. nltk패키지는 단순히이 사용 사례 너무 느렸다.

파이썬으로 구현 한 것은 내가 작성한 클래스의 일부이며 음절 계산 루틴은 아래에 붙여 넣습니다. 조용한 단어 엔딩을 설명하는 좋은 방법을 찾지 못했기 때문에 음절 수를 약간 과대 평가합니다.

이 함수는 Flesch-Kincaid 가독성 점수에 사용되므로 단어 당 음절의 비율을 반환합니다. 숫자는 정확할 필요는 없으며 추정치에 가깝습니다.

7 세대 i7 CPU에서이 함수는 759 단어 샘플 텍스트에 1.1-1.2 밀리 초가 걸렸습니다.

def _countSyllablesEN(self, theText):

    cleanText = ""
    for ch in theText:
        if ch in "abcdefghijklmnopqrstuvwxyz'’":
            cleanText += ch
        else:
            cleanText += " "

    asVow    = "aeiouy'’"
    dExep    = ("ei","ie","ua","ia","eo")
    theWords = cleanText.lower().split()
    allSylls = 0
    for inWord in theWords:
        nChar  = len(inWord)
        nSyll  = 0
        wasVow = False
        wasY   = False
        if nChar == 0:
            continue
        if inWord[0] in asVow:
            nSyll += 1
            wasVow = True
            wasY   = inWord[0] == "y"
        for c in range(1,nChar):
            isVow  = False
            if inWord[c] in asVow:
                nSyll += 1
                isVow = True
            if isVow and wasVow:
                nSyll -= 1
            if isVow and wasY:
                nSyll -= 1
            if inWord[c:c+2] in dExep:
                nSyll += 1
            wasVow = isVow
            wasY   = inWord[c] == "y"
        if inWord.endswith(("e")):
            nSyll -= 1
        if inWord.endswith(("le","ea","io")):
            nSyll += 1
        if nSyll < 1:
            nSyll = 1
        # print("%-15s: %d" % (inWord,nSyll))
        allSylls += nSyll

    return allSylls/len(theWords)

-1

jsoup을 사용 하여이 작업을 한 번 수행했습니다. 다음은 음절 파서 샘플입니다.

public String[] syllables(String text){
        String url = "https://www.merriam-webster.com/dictionary/" + text;
        String relHref;
        try{
            Document doc = Jsoup.connect(url).get();
            Element link = doc.getElementsByClass("word-syllables").first();
            if(link == null){return new String[]{text};}
            relHref = link.html(); 
        }catch(IOException e){
            relHref = text;
        }
        String[] syl = relHref.split("·");
        return syl;
    }

일반적인 음절 파서는 어떻습니까? 이 코드는 사전에서만 음절을 찾는 것 같습니다
— Nico Haase
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.