단어에서 음절 감지
단어로 음절을 감지하는 상당히 효율적인 방법을 찾아야합니다. 예 :
보이지 않는-> 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의 팁은 저에게 효과적입니다.
하이픈 넣기 목적으로이 문제에 대한 TeX 접근 방식에 대해 읽으십시오. 특히 Frank Liang의 논문 논문 Hy-phen-a-tion by Comp-put-er 참조 . 그의 알고리즘은 매우 정확하며 알고리즘이 작동하지 않는 경우에 대한 작은 예외 사전을 포함합니다.
나는이 페이지를 우연히 찾아서 같은 것을 찾고, Liang 논문의 몇 가지 구현을 발견했습니다 : https://github.com/mnater/hyphenator
고유하지 않은 문제에 대해 무료로 사용할 수있는 코드를 적용하는 대신 60 페이지 논문을 읽는 것을 좋아하지 않는 한 그렇지 않습니다. :)
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()]]
텍스트 블록의 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;
}
LaTeX 하이픈 넣기 알고리즘으로 완전히 해결되지 않는 특히 어려운 문제입니다. 사용 가능한 몇 가지 방법과 문제에 대한 요약은 영어 자동 실 라벨 알고리즘 평가 (Marchand, Adsett, Damper 2007)에서 확인할 수 있습니다.
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
@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
누군가가 이것을 유용하게 사용하기를 바랍니다!
왜 계산합니까? 모든 온라인 사전에는이 정보가 있습니다. http://dictionary.reference.com/browse/invisible in · vis · i · ble
Perl에는 Lingua :: Phonology :: Syllable 모듈이 있습니다. 시도해 보거나 알고리즘을 살펴보십시오. 나는 거기에 몇 가지 다른 오래된 모듈도 보았습니다.
정규식이 왜 음절 만 제공하는지 이해하지 못합니다. 캡처 괄호를 사용하여 음절 자체를 얻을 수 있어야합니다. 작동하는 정규식을 구성 할 수 있다고 가정하십시오.
오늘 저는 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;
"\u00AD
API는 normal을 반환하지 않기 때문에 " 로 분할해야합니다 "-"
.
이 접근 방식은 다양한 언어를 지원하고 독일어 하이픈을 더 정확하게 감지하므로 Joe Basirico의 답변보다 성능이 우수합니다.
@ 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.")
음절을 셀 수있는 적절한 방법을 찾지 못해 방법을 직접 설계했습니다.
내 방법은 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"));
Output:
4
3
I ran into this exact same issue a little while ago.
I ended up using the CMU Pronunciation Dictionary for quick and accurate lookups of most words. For words not in the dictionary, I fell back to a machine learning model that's ~98% accurate at predicting syllable counts.
I wrapped the whole thing up in an easy-to-use python module here: https://github.com/repp/big-phoney
Install: pip install big-phoney
Count Syllables:
from big_phoney import BigPhoney
phoney = BigPhoney()
phoney.count_syllables('triceratops') # --> 4
If you're not using Python and you want to try the ML-model-based approach, I did a pretty detailed write up on how the syllable counting model works on Kaggle.
After doing a lot of testing and trying out hyphenation packages as well, I wrote my own based on a number of examples. I also tried the pyhyphen
and pyphen
packages that interfaces with hyphenation dictionaries, but they produce the wrong number of syllables in many cases. The nltk
package was simply too slow for this use case.
My implementation in Python is part of a class i wrote, and the syllable counting routine is pasted below. It over-estimates the number of syllables a bit as I still haven't found a good way to account for silent word endings.
The function returns the ratio of syllables per word as it is used for a Flesch-Kincaid readability score. The number doesn't have to be exact, just close enough for an estimate.
On my 7th generation i7 CPU, this function took 1.1-1.2 milliseconds for a 759 word sample text.
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)
I used jsoup to do this once. Here's a sample syllable parser:
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;
}
참고URL : https://stackoverflow.com/questions/405161/detecting-syllables-in-a-word
'Programing' 카테고리의 다른 글
C를 사용하여 배열 반환 (0) | 2020.07.02 |
---|---|
팬더 : 열의 텍스트를 여러 행으로 나누려면 어떻게합니까? (0) | 2020.07.02 |
HTML 제출 버튼 : 다른 값 / 버튼 텍스트? (0) | 2020.07.02 |
favicon.ico의 올바른 MIME 유형? (0) | 2020.07.01 |
AWS Lambda 예약 된 작업 (0) | 2020.07.01 |