Wordの音節を検出するためのかなり効率的な方法を見つける必要があります。例えば。、
不可視-> in-vi-sib-le
使用できる音節規則がいくつかあります。
V CV VC CVC CCV CCCV CVCC
* Vは母音、Cは子音です。例えば。、
発音(5 Pro-nun-ci-a-tion; CV-CVC-CV-V-CVC)
私はいくつかの方法を試しましたが、そのうち、正規表現(音節を数えたい場合にのみ役立ちます)またはハードコーディングされたルール定義(非常に非効率であることが証明されるブルートフォースアプローチ)を使用し、最終的に有限状態オートマトンを使用しました有用なものではありません)。
私のアプリケーションの目的は、特定の言語ですべての音節の辞書を作成することです。この辞書は、後でスペルチェックアプリケーション(ベイジアン分類子を使用)および音声合成のテキストに使用されます。
以前のアプローチに加えて、この問題を解決する別の方法についてのヒントをいただければ幸いです。
私はJavaで働いていますが、C/C++、C#、Python、Perlのあらゆるヒントが役に立ちます。
ハイフネーションの目的で、この問題に対するTeXのアプローチについて読んでください。特に、Frank Liangの 論文論文Com-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の読み取りスコアを計算するプログラムでこの問題に取り組んでいます。私のアルゴリズムは、このWebサイトで見つけたものを使用します: 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をぶつけます。 perfectではなく、非常に便利な機能ですが、ほとんどの小規模から中規模のプロジェクトに適しています。ジョー、私はあなたのコードの実装をPythonで書き直しました:
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
誰かがこれが役に立つことを願っています!
今日、私は見つけました this Java Frank Liangのハイフネーションアルゴリズムの実装は英語またはドイツ語のパターンで、非常にうまく機能し、Maven Centralで利用可能です。
洞窟:.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;
APIは通常の"\u00AD
を返さないため、"-"
"で分割する必要があります。
このアプローチは、多くの異なる言語をサポートし、ドイツ語のハイフネーションをより正確に検出するため、Joe Basiricoの答えよりも優れています。
なぜ計算するのですか?すべてのオンライン辞書にはこの情報があります。 http://dictionary.reference.com/browse/invisible in・vis・i・ble
Perlには Lingua :: Phonology :: Syllable モジュールがあります。それを試すか、そのアルゴリズムを調べてみてください。私はそこにいくつかの他の古いモジュールも見ました。
正規表現が音節の数だけを与える理由がわかりません。キャプチャ括弧を使用して、音節自体を取得できるはずです。つまり、機能する正規表現を作成できると仮定します。
音節を数える適切な方法が見つからなかったので、自分でメソッドを設計しました。
ここで私のメソッドを見ることができます: 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
@ 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.")
私は少し前にこのまったく同じ問題に遭遇しました。
結局、ほとんどの単語をすばやく正確に検索するために CMU発音辞書 を使用しました。辞書にない単語については、音節数の予測で最大98%正確な機械学習モデルに戻りました。
使いやすいpythonモジュールをここにまとめました: https://github.com/repp/big-phoney
インストール:pip install big-phoney
音節を数える:
from big_phoney import BigPhoney
phoney = BigPhoney()
phoney.count_syllables('triceratops') # --> 4
Pythonを使用しておらず、MLモデルベースのアプローチを試してみたい場合は、かなり詳細に行いました Kaggleでの音節カウントモデルの動作方法について説明します 。
多くのテストを行い、ハイフネーションパッケージも試してみた後、いくつかの例に基づいて独自に作成しました。ハイフネーション辞書と連動するpyhyphen
およびpyphen
パッケージも試しましたが、多くの場合、間違った数の音節が生成されます。 nltk
パッケージは、このユースケースには遅すぎます。
Pythonの私の実装は、私が書いたクラスの一部であり、音節カウントルーチンは以下に貼り付けられます。サイレントWordのエンディングを考慮します。
この関数は、Flesch-Kincaid可読性スコアに使用されるため、Wordごとの音節の比率を返します。数値は正確である必要はなく、推定に十分近い値です。
私の第7世代i7 CPUでは、この関数は759 Wordのサンプルテキストに対して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)