そのタスクは単純です。これは、テキストデータが書かれているいくつかの言語を教えてくれます。これは、テキスト分類やスペルチェックなどの自然言語処理アプリケーションの言語データの前処理ステップとして非常に便利です。たとえば、他のユースケースには、電子メールの言語に基づいて、地理的に配置された適切なカスタマーサービス部門への電子メールをルーティングすることが含まれる場合があります。
言語の検出は、多くの場合、大規模な機械学習フレームワークまたは自然言語処理アプリケーションの一部として行われます。これらのシステムの本格的な機能を必要としない場合、またはそれらのロープを学びたくない場合、小さな柔軟なライブラリが便利です。
これまでのところ、このタスクのためにJVMに取り組んでいる他の3つの包括的なオープンソースライブラリは、Apache Tika、Apache OpenNLP、および最適化言語検出器です。残念ながら、特に後者には3つの大きな欠点があります。
Linguaは、これらの問題を排除することを目指しています。構成はほとんど必要ありませんし、単一の単語やフレーズであっても、長いテキストと短いテキストの両方でかなり正確な結果が得られます。ルールベースと統計の両方の方法を利用しますが、単語の辞書は使用しません。外部APIまたはサービスへの接続も必要ありません。ライブラリがダウンロードされると、完全にオフラインで使用できます。
他の言語検出ライブラリと比較して、 Linguaの焦点は量よりも品質、つまり、新しい言語を追加する前に、最初に小さな言語セットの検出を正しく取得することです。現在、次の75の言語がサポートされています。
Linguaは、サポートされている各言語で利用可能ないくつかのバンドルされたテストデータの精度統計を報告できます。各言語のテストデータは3つの部分に分割されます。
言語モデルとテストデータの両方は、ドイツのライプツィヒ大学が提供したWortschatz Corporaの個別の文書から作成されています。さまざまなニュースWebサイトからクロールされたデータがトレーニングに使用されており、各コーパスは100万文を構成しています。テストのために、任意に選択したWebサイトで作られたコーパスが使用されており、それぞれ1万件の文で構成されています。各テストコーパスから、それぞれ1000単語のランダムなアンソートサブセット、1000ワードのペア、1000文が抽出されています。
生成されたテストデータを考慮して、 Lingua 、 Apache Tika 、 Apache OpenNLPの検出結果を比較し、 Linguaのサポートされている75言語のデータを実行しているパラメーター化されたJunitテストを使用して言語検出器を最適化しました。他のライブラリによってサポートされていない言語は、検出プロセス中に単に無視されます。
次の各セクションには、2つのプロットが含まれています。バープロットには、サポートされている各言語の詳細な精度結果が表示されます。ボックスプロットは、各分類器の精度値の分布を示しています。ボックス自体は、データの中央50%が内部にある領域を表しています。色付きのボックス内では、水平線が分布の中央値をマークします。
以下の表は、平均、中央値、標準偏差を含む各言語および分類器の詳細な統計を示しています。
| 言語 | 平均 | 単一の単語 | 単語のペア | 文 | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| リングア (高精度モード) | リングア (低精度モード) | ティカ | opennlp | 最適化します | リングア (高精度モード) | リングア (低精度モード) | ティカ | opennlp | 最適化します | リングア (高精度モード) | リングア (低精度モード) | ティカ | opennlp | 最適化します | リングア (高精度モード) | リングア (低精度モード) | ティカ | opennlp | 最適化します | |
| アフリカーンス | 79 | 64 | 71 | 72 | 39 | 58 | 38 | 44 | 41 | 3 | 81 | 62 | 70 | 75 | 22 | 97 | 93 | 98 | 99 | 93 |
| アルバニア語 | 88 | 80 | 79 | 71 | 70 | 69 | 54 | 54 | 40 | 38 | 95 | 86 | 84 | 73 | 73 | 100 | 99 | 99 | 100 | 98 |
| アラビア語 | 98 | 94 | 97 | 84 | 89 | 96 | 88 | 94 | 65 | 72 | 99 | 96 | 99 | 88 | 94 | 100 | 99 | 100 | 99 | 100 |
| アルメニア人 | 100 | 100 | - | 100 | - | 100 | 100 | - | 100 | - | 100 | 100 | - | 100 | - | 100 | 100 | - | 100 | - |
| アゼルバイジャン | 90 | 82 | - | 82 | - | 77 | 71 | - | 60 | - | 92 | 78 | - | 86 | - | 99 | 96 | - | 99 | - |
| バスク | 84 | 74 | 83 | 77 | 66 | 71 | 56 | 64 | 56 | 33 | 87 | 76 | 86 | 82 | 70 | 93 | 91 | 98 | 92 | 95 |
| ベラルーシ人 | 97 | 92 | 96 | 91 | 87 | 92 | 80 | 92 | 78 | 69 | 99 | 95 | 98 | 95 | 92 | 100 | 100 | 100 | 100 | 99 |
| ベンガル語 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| ボクマル | 58 | 49 | - | 66 | - | 39 | 27 | - | 42 | - | 59 | 47 | - | 69 | - | 75 | 74 | - | 87 | - |
| ボスニア人 | 35 | 29 | - | 26 | - | 29 | 23 | - | 12 | - | 35 | 29 | - | 22 | - | 40 | 36 | - | 44 | - |
| ブルガリア | 87 | 78 | 73 | 83 | 48 | 70 | 56 | 52 | 62 | 18 | 91 | 81 | 69 | 87 | 36 | 99 | 96 | 96 | 100 | 91 |
| カタロニア | 70 | 58 | 58 | 42 | 31 | 51 | 33 | 32 | 11 | 2 | 74 | 60 | 57 | 32 | 16 | 86 | 81 | 84 | 81 | 77 |
| 中国語 | 100 | 100 | 69 | 78 | 31 | 100 | 100 | 20 | 40 | 0 | 100 | 100 | 86 | 94 | 2 | 100 | 100 | 100 | 100 | 91 |
| クロアチア語 | 72 | 60 | 74 | 50 | 41 | 53 | 36 | 54 | 23 | 8 | 74 | 57 | 72 | 44 | 24 | 90 | 85 | 97 | 81 | 91 |
| チェコ | 80 | 71 | 72 | 67 | 49 | 66 | 54 | 54 | 42 | 21 | 84 | 72 | 75 | 70 | 46 | 91 | 87 | 88 | 90 | 81 |
| デンマーク語 | 81 | 70 | 83 | 60 | 55 | 61 | 45 | 63 | 34 | 19 | 84 | 70 | 86 | 52 | 51 | 98 | 95 | 99 | 94 | 96 |
| オランダ語 | 77 | 64 | 60 | 61 | 39 | 55 | 36 | 31 | 31 | 6 | 81 | 61 | 52 | 57 | 19 | 96 | 94 | 98 | 97 | 91 |
| 英語 | 81 | 62 | 64 | 52 | 41 | 55 | 29 | 30 | 10 | 2 | 89 | 62 | 62 | 46 | 23 | 99 | 96 | 99 | 99 | 97 |
| エスペラント | 84 | 66 | - | 76 | - | 67 | 44 | - | 50 | - | 85 | 61 | - | 78 | - | 98 | 92 | - | 100 | - |
| エストニアン | 92 | 83 | 84 | 59 | 61 | 80 | 62 | 66 | 29 | 23 | 96 | 88 | 88 | 60 | 63 | 100 | 99 | 100 | 88 | 98 |
| フィンランド語 | 96 | 91 | 94 | 86 | 79 | 90 | 77 | 86 | 68 | 51 | 98 | 95 | 96 | 91 | 86 | 100 | 100 | 100 | 100 | 100 |
| フランス語 | 89 | 77 | 78 | 59 | 54 | 74 | 52 | 55 | 25 | 18 | 94 | 83 | 80 | 55 | 48 | 99 | 97 | 99 | 98 | 97 |
| ガンダ | 91 | 84 | - | - | - | 79 | 65 | - | - | - | 95 | 87 | - | - | - | 100 | 100 | - | - | - |
| ジョージアン | 100 | 100 | - | 100 | - | 100 | 100 | - | 100 | - | 100 | 100 | - | 100 | - | 100 | 100 | - | 100 | - |
| ドイツ語 | 89 | 80 | 74 | 67 | 55 | 74 | 57 | 50 | 38 | 21 | 94 | 84 | 71 | 66 | 46 | 100 | 99 | 100 | 98 | 99 |
| ギリシャ語 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| グジャラート語 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| ヘブライ語 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| ヒンディー語 | 73 | 33 | 80 | 58 | 51 | 61 | 11 | 65 | 28 | 16 | 64 | 20 | 75 | 49 | 38 | 93 | 67 | 99 | 99 | 98 |
| ハンガリー | 95 | 90 | 88 | 78 | 77 | 87 | 77 | 75 | 53 | 51 | 98 | 94 | 91 | 82 | 82 | 100 | 100 | 100 | 100 | 99 |
| アイスランド語 | 93 | 88 | 90 | 76 | 78 | 83 | 72 | 76 | 53 | 53 | 97 | 92 | 94 | 76 | 82 | 100 | 99 | 100 | 99 | 99 |
| インドネシア語 | 60 | 48 | 60 | 29 | 18 | 39 | 25 | 37 | 10 | 0 | 61 | 46 | 62 | 25 | 1 | 81 | 72 | 82 | 52 | 54 |
| アイルランド | 91 | 85 | 90 | 78 | 80 | 82 | 70 | 80 | 56 | 58 | 94 | 90 | 92 | 82 | 85 | 96 | 95 | 99 | 97 | 98 |
| イタリア語 | 87 | 71 | 80 | 64 | 51 | 69 | 42 | 58 | 31 | 12 | 92 | 74 | 84 | 61 | 43 | 100 | 98 | 99 | 100 | 98 |
| 日本語 | 100 | 100 | 25 | 95 | 98 | 100 | 100 | 1 | 87 | 99 | 100 | 100 | 5 | 100 | 100 | 100 | 100 | 68 | 100 | 96 |
| カザフ | 92 | 90 | - | 85 | - | 80 | 78 | - | 66 | - | 96 | 93 | - | 90 | - | 99 | 99 | - | 100 | - |
| 韓国語 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| ラテン | 87 | 73 | - | 70 | - | 72 | 49 | - | 43 | - | 93 | 76 | - | 71 | - | 97 | 93 | - | 96 | - |
| ラトビアン | 93 | 87 | 90 | 86 | 78 | 85 | 75 | 78 | 72 | 56 | 97 | 90 | 93 | 88 | 82 | 99 | 97 | 98 | 98 | 97 |
| リトアニアン | 95 | 87 | 89 | 79 | 72 | 86 | 76 | 74 | 56 | 40 | 98 | 89 | 92 | 83 | 77 | 100 | 98 | 99 | 99 | 98 |
| マケドニア語 | 84 | 72 | 83 | 68 | 46 | 66 | 52 | 66 | 37 | 10 | 86 | 70 | 83 | 68 | 32 | 99 | 95 | 100 | 98 | 97 |
| マレー | 31 | 31 | 23 | 19 | 4 | 26 | 22 | 19 | 10 | 0 | 38 | 36 | 22 | 20 | 0 | 30 | 36 | 28 | 27 | 11 |
| マオリ | 92 | 83 | - | 92 | - | 84 | 64 | - | 85 | - | 92 | 88 | - | 90 | - | 99 | 98 | - | 100 | - |
| マラーティー | 85 | 41 | 90 | 81 | 71 | 74 | 20 | 81 | 62 | 43 | 85 | 30 | 92 | 83 | 74 | 96 | 72 | 98 | 98 | 96 |
| モンゴル人 | 97 | 96 | - | 84 | - | 93 | 89 | - | 66 | - | 99 | 98 | - | 88 | - | 99 | 99 | - | 99 | - |
| Nynorsk | 66 | 52 | - | 55 | - | 41 | 25 | - | 24 | - | 66 | 49 | - | 47 | - | 90 | 81 | - | 92 | - |
| ペルシャ語 | 90 | 80 | 81 | 75 | 62 | 78 | 62 | 65 | 53 | 29 | 94 | 80 | 79 | 74 | 58 | 100 | 98 | 99 | 99 | 99 |
| 研磨 | 95 | 90 | 90 | 83 | 81 | 85 | 77 | 76 | 61 | 57 | 98 | 93 | 93 | 89 | 86 | 100 | 99 | 100 | 100 | 100 |
| ポルトガル語 | 81 | 69 | 63 | 58 | 40 | 59 | 42 | 34 | 22 | 7 | 85 | 70 | 58 | 54 | 19 | 98 | 95 | 98 | 98 | 94 |
| パンジャブ | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| ルーマニア人 | 87 | 72 | 78 | 67 | 55 | 69 | 49 | 57 | 34 | 24 | 92 | 74 | 80 | 68 | 50 | 99 | 94 | 97 | 99 | 91 |
| ロシア | 90 | 78 | 80 | 50 | 53 | 76 | 59 | 62 | 20 | 22 | 95 | 84 | 85 | 43 | 50 | 98 | 92 | 94 | 86 | 87 |
| セルビア人 | 88 | 78 | 73 | 73 | 46 | 74 | 62 | 57 | 46 | 18 | 90 | 80 | 70 | 74 | 39 | 99 | 91 | 90 | 98 | 80 |
| ショナ | 91 | 81 | - | - | - | 78 | 56 | - | - | - | 96 | 86 | - | - | - | 100 | 100 | - | - | - |
| スロバキア | 84 | 75 | 76 | 70 | 47 | 64 | 49 | 53 | 39 | 12 | 90 | 78 | 76 | 73 | 38 | 99 | 97 | 98 | 99 | 92 |
| スロベニア | 82 | 67 | 74 | 71 | 37 | 61 | 39 | 53 | 43 | 3 | 87 | 68 | 72 | 72 | 18 | 99 | 93 | 98 | 99 | 90 |
| ソマリア | 92 | 85 | 91 | 69 | 79 | 82 | 64 | 78 | 35 | 50 | 96 | 90 | 94 | 74 | 88 | 100 | 100 | 100 | 98 | 100 |
| ソソー | 85 | 72 | - | - | - | 67 | 43 | - | - | - | 90 | 75 | - | - | - | 99 | 97 | - | - | - |
| スペイン語 | 70 | 56 | 59 | 42 | 32 | 44 | 26 | 29 | 8 | 0 | 69 | 49 | 50 | 25 | 6 | 97 | 94 | 97 | 93 | 91 |
| スワヒリ | 81 | 70 | 75 | 73 | 60 | 60 | 43 | 50 | 45 | 26 | 84 | 68 | 75 | 74 | 58 | 98 | 97 | 99 | 99 | 98 |
| スウェーデン語 | 84 | 72 | 71 | 69 | 50 | 64 | 46 | 44 | 41 | 15 | 88 | 76 | 72 | 69 | 42 | 99 | 95 | 97 | 97 | 94 |
| タガログ | 78 | 66 | 77 | 61 | 61 | 52 | 36 | 53 | 27 | 23 | 83 | 67 | 79 | 57 | 62 | 99 | 96 | 99 | 98 | 97 |
| タミル語 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| テルグ語 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| タイ | 99 | 99 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 98 | 98 | 100 | 99 | 100 |
| ツンガ | 84 | 72 | - | - | - | 66 | 46 | - | - | - | 89 | 73 | - | - | - | 98 | 97 | - | - | - |
| ツワナ | 84 | 71 | - | - | - | 65 | 44 | - | - | - | 88 | 73 | - | - | - | 99 | 96 | - | - | - |
| トルコ語 | 94 | 87 | 81 | 72 | 70 | 84 | 71 | 62 | 48 | 43 | 98 | 91 | 83 | 71 | 70 | 100 | 99 | 99 | 98 | 96 |
| ウクライナ人 | 92 | 86 | 81 | 79 | 68 | 84 | 75 | 62 | 54 | 39 | 97 | 92 | 84 | 83 | 69 | 95 | 93 | 97 | 99 | 94 |
| ウルドゥー語 | 91 | 80 | 83 | 68 | 72 | 80 | 65 | 68 | 45 | 49 | 94 | 78 | 84 | 62 | 71 | 98 | 96 | 96 | 98 | 96 |
| ベトナム人 | 91 | 87 | 85 | 84 | 87 | 79 | 76 | 63 | 66 | 65 | 94 | 87 | 92 | 86 | 95 | 99 | 98 | 100 | 100 | 100 |
| ウェールズ | 91 | 82 | 85 | 77 | 77 | 78 | 61 | 68 | 50 | 50 | 96 | 87 | 88 | 81 | 82 | 99 | 99 | 100 | 99 | 99 |
| xhosa | 82 | 69 | - | - | - | 64 | 45 | - | - | - | 85 | 67 | - | - | - | 98 | 94 | - | - | - |
| ヨルバ | 75 | 62 | - | - | - | 50 | 33 | - | - | - | 77 | 61 | - | - | - | 97 | 93 | - | - | - |
| ズールー | 81 | 70 | - | 78 | - | 62 | 45 | - | 51 | - | 83 | 72 | - | 82 | - | 97 | 94 | - | 100 | - |
| 平均 | 86 | 77 | 80 | 74 | 65 | 74 | 61 | 64 | 53 | 41 | 89 | 78 | 81 | 74 | 61 | 96 | 93 | 96 | 95 | 93 |
| 中央値 | 89.23 | 79.63 | 81.3 | 75.55 | 63.85 | 74.3 | 56.7 | 63.39 | 48.7 | 30.75 | 93.7 | 80.6 | 84.25 | 75.55 | 65.95 | 99.0 | 96.9 | 99.15 | 99.0 | 97.4 |
| 標準偏差 | 13.12 | 17.26 | 16.2 | 18.56 | 23.87 | 18.43 | 24.81 | 23.9 | 27.37 | 33.87 | 13.13 | 18.96 | 18.74 | 21.32 | 31.32 | 11.02 | 11.86 | 10.77 | 12.59 | 13.54 |
すべての言語検出器は、一部のトレーニングコーパスのキャラクター分布で訓練された確率的N-GRAMモデルを使用します。ほとんどのライブラリは、複数の文で構成される長いテキストフラグメントの言語を検出するのに満足できるサイズ3(Trigrams)のNグラムのみを使用しています。ただし、短いフレーズや単一の単語の場合、トリグラムでは十分ではありません。入力テキストが短いほど、N-Gramsが少なくなります。このような少数のNグラムから推定される確率は信頼できません。これが、 Linguaがサイズ1のn-gramsを最大5で使用している理由であり、その結果、正しい言語のはるかに正確な予測が生じます。
2番目の重要な違いは、 Linguaがこのような統計モデルだけでなく、ルールベースのエンジンも使用することです。このエンジンは、最初に入力テキストのアルファベットを決定し、1つ以上の言語でユニークな文字を検索します。この方法で正確に1つの言語を確実に選択できる場合、統計モデルはもう必要ありません。いずれにせよ、ルールベースのエンジンは、入力テキストの条件を満たさない言語を除外します。その場合にのみ、2番目のステップでは、確率的N-GRAMモデルが考慮されます。これは理にかなっています。これは、言語モデルのロードが少ないため、メモリ消費量が少なく、ランタイムパフォーマンスが向上するためです。
一般に、それぞれのAPIメソッドを使用して、分類プロセスで考慮される言語のセットを制限することを常に良い考えです。特定の言語が入力テキストで発生することはないことを事前に知っている場合は、分類プロセスに参加しないようにしてください。ルールベースのエンジンのフィルタリングメカニズムは非常に優れていますが、入力テキストに関する独自の知識に基づいたフィルタリングが常に望ましいです。
上記の精度の結果を再現したい場合は、4つの分類子すべてとすべての言語のテストレポートを自分で生成できます。
./gradlew accuracyReport
また、Gradleタスクに引数を渡すことにより、分類子と言語を制限してレポートを生成することもできます。次のタスクは、言語と言語の英語とドイツ語のみのレポートを生成します。
./gradlew accuracyReport -Pdetectors=Lingua -Planguages=English,German
デフォルトでは、レポート生成には単一のCPUコアのみが使用されます。マシンにマルチコアCPUがある場合は、CPUコアと同じくらい多くのプロセスをフォークできます。これにより、レポートの生成が大幅に高速化されます。ただし、複数のプロセスを分岐すると、大量のRAMが消費される可能性があることに注意してください。あなたはこのようにそれをします:
./gradlew accuracyReport -PcpuCores=2
各検出器と言語について、テストレポートファイルが/accuracy-reportsに書き込まれ、 srcディレクトリの横にあります。例として、 Linguaドイツのレポートの現在の出力は次のとおりです。
##### GERMAN #####
Legend: 'low accuracy mode | high accuracy mode'
>>> Accuracy on average: 79.80% | 89.23%
>> Detection of 1000 single words (average length: 9 chars)
Accuracy: 56.70% | 73.90%
Erroneously classified as DUTCH: 2.80% | 2.30%, DANISH: 2.20% | 2.10%, ENGLISH: 1.90% | 2.00%, LATIN: 1.90% | 1.90%, BOKMAL: 2.40% | 1.60%, BASQUE: 1.60% | 1.20%, ITALIAN: 1.00% | 1.20%, FRENCH: 1.60% | 1.20%, ESPERANTO: 1.10% | 1.10%, SWEDISH: 3.20% | 1.00%, AFRIKAANS: 1.30% | 0.80%, TSONGA: 1.50% | 0.70%, NYNORSK: 1.40% | 0.60%, PORTUGUESE: 0.50% | 0.60%, YORUBA: 0.40% | 0.60%, SOTHO: 0.70% | 0.50%, FINNISH: 0.80% | 0.50%, WELSH: 1.30% | 0.50%, SPANISH: 1.20% | 0.40%, SWAHILI: 0.60% | 0.40%, TSWANA: 2.20% | 0.40%, POLISH: 0.70% | 0.40%, ESTONIAN: 0.90% | 0.40%, IRISH: 0.50% | 0.40%, TAGALOG: 0.10% | 0.30%, ICELANDIC: 0.30% | 0.30%, BOSNIAN: 0.10% | 0.30%, LITHUANIAN: 0.80% | 0.20%, MAORI: 0.50% | 0.20%, INDONESIAN: 0.40% | 0.20%, ALBANIAN: 0.60% | 0.20%, CATALAN: 0.70% | 0.20%, ZULU: 0.30% | 0.20%, ROMANIAN: 1.20% | 0.20%, CROATIAN: 0.10% | 0.20%, XHOSA: 0.40% | 0.20%, TURKISH: 0.70% | 0.10%, MALAY: 0.50% | 0.10%, LATVIAN: 0.40% | 0.10%, SLOVENE: 0.00% | 0.10%, SLOVAK: 0.30% | 0.10%, SOMALI: 0.00% | 0.10%, HUNGARIAN: 0.40% | 0.00%, SHONA: 0.80% | 0.00%, VIETNAMESE: 0.40% | 0.00%, CZECH: 0.30% | 0.00%, GANDA: 0.20% | 0.00%, AZERBAIJANI: 0.10% | 0.00%
>> Detection of 1000 word pairs (average length: 18 chars)
Accuracy: 83.50% | 94.10%
Erroneously classified as DUTCH: 1.50% | 0.90%, LATIN: 1.00% | 0.80%, ENGLISH: 1.40% | 0.70%, SWEDISH: 1.40% | 0.60%, DANISH: 1.20% | 0.50%, FRENCH: 0.60% | 0.40%, BOKMAL: 1.40% | 0.30%, TAGALOG: 0.10% | 0.20%, IRISH: 0.20% | 0.20%, TURKISH: 0.10% | 0.10%, NYNORSK: 0.90% | 0.10%, TSONGA: 0.40% | 0.10%, ZULU: 0.10% | 0.10%, ESPERANTO: 0.30% | 0.10%, AFRIKAANS: 0.60% | 0.10%, ITALIAN: 0.10% | 0.10%, ESTONIAN: 0.30% | 0.10%, FINNISH: 0.40% | 0.10%, SOMALI: 0.00% | 0.10%, SWAHILI: 0.20% | 0.10%, MAORI: 0.00% | 0.10%, WELSH: 0.10% | 0.10%, LITHUANIAN: 0.40% | 0.00%, INDONESIAN: 0.10% | 0.00%, CATALAN: 0.30% | 0.00%, LATVIAN: 0.20% | 0.00%, XHOSA: 0.30% | 0.00%, SPANISH: 0.50% | 0.00%, MALAY: 0.10% | 0.00%, SLOVAK: 0.10% | 0.00%, BASQUE: 0.40% | 0.00%, YORUBA: 0.20% | 0.00%, TSWANA: 0.30% | 0.00%, SHONA: 0.10% | 0.00%, PORTUGUESE: 0.10% | 0.00%, SOTHO: 0.30% | 0.00%, CZECH: 0.10% | 0.00%, ALBANIAN: 0.40% | 0.00%, AZERBAIJANI: 0.10% | 0.00%, ICELANDIC: 0.10% | 0.00%, SLOVENE: 0.10% | 0.00%
>> Detection of 1000 sentences (average length: 111 chars)
Accuracy: 99.20% | 99.70%
Erroneously classified as DUTCH: 0.00% | 0.20%, LATIN: 0.20% | 0.10%, NYNORSK: 0.10% | 0.00%, SPANISH: 0.10% | 0.00%, DANISH: 0.10% | 0.00%, SOTHO: 0.20% | 0.00%, ZULU: 0.10% | 0.00%
LinguaはGithub PackagesとMaven Centralでホストされています。
// Groovy syntax
implementation 'com.github.pemistahl:lingua:1.2.2'
// Kotlin syntax
implementation("com.github.pemistahl:lingua:1.2.2")
<dependency>
<groupId>com.github.pemistahl</groupId>
<artifactId>lingua</artifactId>
<version>1.2.2</version>
</dependency>
LinguaはGradleを使用して構築し、そのためにJava> = 1.8を必要とします。
git clone https://github.com/pemistahl/lingua.git
cd lingua
./gradlew build
プロジェクトからいくつかのJARアーカイブを作成できます。
./gradlew jar 、コンパイルされたソースのみを含むlingua-1.2.2.jar ../gradlew sourcesJar 、プレーンソースコードを含むlingua-1.2.2-sources.jarを組み立てます。./gradlew jarWithDependencies lingua-1.2.2-with-dependencies.jarを組み立てます。このJARファイルは、依存関係管理システムなしでプロジェクトに含めることができます。また、スタンドアロンモードでLinguaを実行するためにも使用できます(以下を参照)。 Linguaは、独自のコードまたはスタンドアロンモードでプログラムで使用できます。
APIは非常に簡単で、KotlinコードとJavaコードの両方で使用できます。
/* Kotlin */
import com.github.pemistahl.lingua.api.*
import com.github.pemistahl.lingua.api.Language.*
val detector : LanguageDetector = LanguageDetectorBuilder .fromLanguages( ENGLISH , FRENCH , GERMAN , SPANISH ).build()
val detectedLanguage : Language = detector.detectLanguageOf(text = " languages are awesome " ) LinguaのパブリックAPIは、どこかでnullを返すことはないため、Javaコード内からも使用しても安全です。
/* Java */
import com . github . pemistahl . lingua . api .*;
import static com . github . pemistahl . lingua . api . Language .*;
final LanguageDetector detector = LanguageDetectorBuilder . fromLanguages ( ENGLISH , FRENCH , GERMAN , SPANISH ). build ();
final Language detectedLanguage = detector . detectLanguageOf ( "languages are awesome" );デフォルトでは、 Linguaは特定の入力テキストの最も可能性の高い言語を返します。ただし、複数の言語で同じように綴られている特定の単語があります。たとえば、プロローグという言葉は、有効な英語とフランス語の両方の言葉です。 Linguaは、指定されたコンテキストで間違っている可能性のある英語またはフランス語のいずれかを出力します。そのようなケースの場合、各可能な言語の対数が満たさなければならないでしょう、および要約された可能性が満たさなければならない最小相対距離を指定することが可能です。次のように述べることができます。
val detector = LanguageDetectorBuilder
.fromAllLanguages()
.withMinimumRelativeDistance( 0.25 ) // minimum: 0.00 maximum: 0.99 default: 0.00
.build()言語確率間の距離は、入力テキストの長さに依存していることに注意してください。入力テキストが長いほど、言語間の距離が大きくなります。したがって、非常に短いテキストフレーズを分類したい場合は、最小相対距離を高すぎないように設定しないでください。それ以外の場合、ほとんどの結果はLanguage.UNKNOWNとして返されます。
最も可能性の高い言語について知ることはいいことですが、計算された可能性はどれほど信頼できますか?そして、最も可能性の高い言語と比較して、他の審査された言語はどれほど少ないでしょうか?これらの質問にも答えることができます:
val detector = LanguageDetectorBuilder .fromLanguages( GERMAN , ENGLISH , FRENCH , SPANISH ).build()
val confidenceValues = detector.computeLanguageConfidenceValues(text = " Coding is fun. " )
// {
// ENGLISH=1.0,
// GERMAN=0.8665738136456169,
// FRENCH=0.8249537317466078,
// SPANISH=0.7792362923625288
// }上記の例では、すべての可能な言語のマップが返され、下降順序の信頼値によってソートされます。検出器が計算する値は、絶対的なメトリックではなく、相対的な信頼メトリックの一部です。各値は0.0〜1.0の数です。最も可能性の高い言語は、常に値1.0で返されます。他のすべての言語は、1.0未満の値が割り当てられている値を取得します。これは、最も可能性の高い言語と比較して、これらの言語の可能性がどれほど少ないかを示します。
この方法で返されたマップには、 LanguageDetectorの呼び出しインスタンスが構築されたすべての言語が必ずしも含まれていません。ルールベースのエンジンが特定の言語が本当に不可能であると判断した場合、それは返されたマップの一部ではありません。同様に、指定された入力テキストの検出器の言語内にNGRAMの確率が見つからない場合、返されたマップは空になります。返されたマップの一部ではない各言語の信頼値は、0.0であると想定されます。
デフォルトでは、 LinguaはLazy-Loadingを使用して、ルールベースのフィルターエンジンによって関連すると見なされる需要のある言語モデルのみをロードします。たとえば、Webサービスの場合、すべての言語モデルをメモリにプリロードして、サービスの応答を待っている間に予期しない遅延を回避することがかなり有益です。熱心なロードモードを有効にしたい場合は、次のように行うことができます。
LanguageDetectorBuilder .fromAllLanguages().withPreloadedLanguageModels().build() LanguageDetectorの複数のインスタンスは、インスタンスによって非同期にアクセスされる同じ言語モデルをメモリ内で共有します。
Linguaの高い検出精度は、他の言語検出器よりも著しく遅くなるという犠牲を払っています。大規模な言語モデルは、かなりの量のメモリも消費します。これらの要件は、リソースを低く走るシステムでは実行不可能な場合があります。ほとんど長いテキストを分類するか、リソースを保存する必要がある場合は、言語モデルの小さなサブセットのみをメモリにロードする低精度モードを有効にできます。
LanguageDetectorBuilder .fromAllLanguages().withLowAccuracyMode().build()このアプローチの欠点は、120文字未満で構成される短いテキストの検出精度が大幅に低下することです。ただし、120文字より長いテキストの検出精度は、ほとんど影響を受けません。
メモリのフットプリントが小さく、パフォーマンスが高速化するための代替案は、言語検出器を構築するときに言語のセットを削減することです。ほとんどの場合、サポートされているすべての言語から検出器を構築することはお勧めできません。分類したいテキストについての知識がある場合、ほとんどの場合、特定の言語を不可能または発生する可能性が低いと除外できます。
たとえば、あなたの言語データがラテン語で間違いなく書かれていないことを事前に知っている分類タスクがあるかもしれません(なんて驚きです:-)。決定プロセスから特定の言語を除外する場合、または関連する言語を明示的に含める場合、そのような場合、検出精度はより良くなる可能性があります。
// include all languages available in the library
// WARNING: in the worst case this produces high memory
// consumption of approximately 3.5GB
// and slow runtime performance
// (in high accuracy mode)
LanguageDetectorBuilder .fromAllLanguages()
// include only languages that are not yet extinct (= currently excludes Latin)
LanguageDetectorBuilder .fromAllSpokenLanguages()
// include only languages written with Cyrillic script
LanguageDetectorBuilder .fromAllLanguagesWithCyrillicScript()
// exclude only the Spanish language from the decision algorithm
LanguageDetectorBuilder .fromAllLanguagesWithout( Language . SPANISH )
// only decide between English and German
LanguageDetectorBuilder .fromLanguages( Language . ENGLISH , Language . GERMAN )
// select languages by ISO 639-1 code
LanguageDetectorBuilder .fromIsoCodes639_1( IsoCode639_1 . EN , IsoCode639_3 . DE )
// select languages by ISO 639-3 code
LanguageDetectorBuilder .fromIsoCodes639_3( IsoCode639_3 . ENG , IsoCode639_3 . DEU )内部的には、 Linguaは言語モデルと言語検出自体のロードをスピードアップするために、CPUのすべてのコアを効率的に使用します。この目的のために、内部Forkjoinpoolが使用されます。アプリケーションサーバー内でライブラリが使用されている場合、アプリケーションが展開されていない場合、消費されたメモリは自動的に解放されません。
Linguaのすべてのリソースをすべて解放したい場合は、解除中にdetector.unloadLanguageModels()を呼び出してこれを手動で行う必要があります。これにより、メモリからロードされたすべての言語モデルがクリアされますが、スレッドプールは実行され続けます。
使用するかどうかを決定する前にLinguaを試してみたい場合は、それをREPLで実行し、すぐにその検出結果を確認できます。
./gradlew runLinguaOnConsole --console=plainjava -jar lingua-1.2.2-with-dependencies.jarその後、ただ遊んでください:
This is Lingua.
Select the language models to load.
1: enter language iso codes manually
2: all supported languages
Type a number and press <Enter>.
Type :quit to exit.
> 1
List some language iso 639-1 codes separated by spaces and press <Enter>.
Type :quit to exit.
> en fr de es
Loading language models...
Done. 4 language models loaded lazily.
Type some text and press <Enter> to detect its language.
Type :quit to exit.
> languages
ENGLISH
> Sprachen
GERMAN
> langues
FRENCH
> :quit
Bye! Ciao! Tschüss! Salut!
Linguaに何かを提供したい場合は、そうすることをお勧めします。 APIを改善するためのアイデアはありますか?早めにサポートしたい特定の言語はありますか?それとも、これまでにバグを見つけましたか?お気軽に問題を開くか、プルリクエストを送信してください。とても感謝しています。
プルリクエストについては、すべてのユニットテストが合格し、公式のコトリンスタイルガイドに従ってコードがフォーマットされていることを確認してください。これは./gradlew ktlintCheckを使用してKotlin Linter Ktlintを実行して確認できます。リナーが識別するほとんどの問題は、 ./gradlew ktlintFormat実行することで修正できます。他のすべての問題、特に120文字より長い行は、自動的に修正できません。この場合、それぞれの行を手作業でフォーマットしてください。フォーマットが正しくない場合、ビルドが失敗することに気付くでしょう。
あらゆる種類のプルリクエストは大歓迎です。最も好きなものは、新しい言語の追加です。 Linguaに新しい言語を提供したい場合は、それを達成する方法を説明する詳細なマニュアルがあります。
あらゆる貢献をありがとうございました。
以下の手順を実行するには、Java 8以下が必要です。ライブラリ自体はJava> = 6で実行されていますが、 FilesWriterクラスはJava 8で導入されたJava.nio APIを使用します。
IsoCode639_1およびIsoCode639_3を開き、言語のISOコードを追加します。他のサイトの中でも、ウィキペディアは包括的なリストを提供しています。Languageを開き、言語の新しいエントリを追加します。言語がLinguaのAlphabet Enumでまだサポートされていないスクリプトで書かれている場合は、そこに新しいエントリも追加します。Language列挙のそれぞれのエントリにそれらを追加します。ただし、文字が複数の言語で発生しているが、すべての言語では発生しない場合は、代わりにクラスConstantで定数CHARS_TO_LANGUAGES_MAPPINGに追加します。LanguageModelFilesWriterを使用して、言語モデルファイルを作成します。 NGRAM確率の推定に使用されるトレーニングデータファイルは、有効なTXTファイルである以外の特定の形式を持つ必要はありません。/src/main/resources/language-modelsに新しいサブディレクトリを作成し、生成された言語モデルファイルをそこに配置します。言語モデルファイルの名前を変更しないでください。サブディレクトリの名前は、言語のISO 639-1コードである必要があり、完全に低く及びます。TestDataFilesWriterを使用して、精度レポート生成に使用されるテストデータファイルを作成します。テストデータを作成するための入力ファイルには、各文が個別の行にある必要があります。/src/accuracyReport/resources/language-testdataに入れます。テストデータファイルの名前を変更しないでください。/src/accuracyReport/kotlin/com/github/pemistahl/lingua/report/configの抽象的なベースクラスを作成します。このディレクトリの他の言語のファイルを見て、クラスがどのように見えるかを確認してください。それはかなり自明でなければなりません。/src/accuracyReport/kotlin/com/github/pemistahl/lingua/report/linguaでコンクリートテストクラスを作成します。このディレクトリの他の言語のファイルを見て、クラスがどのように見えるかを確認してください。それはかなり自明でなければなりません。他の言語検出器ライブラリのいずれかがすでにあなたの言語をサポートしている場合、それらのテストクラスも追加できます。各ライブラリには、この目的のための独自のディレクトリがあります。他の言語検出器ライブラリによって言語がサポートされていない場合は、 AbstractLanguageDetectionAccuracyReportで除外してください。/gradle.propertiesのプロパティlinguaSupportedLanguagesに新しい言語を追加します。./gradlew accuracyReportを実行し、更新された精度レポートをプルリクエストに追加します。./gradlew drawAccuracyPlotsを実行し、更新されたプロットをプルリクエストに追加します。./gradlew writeAccuracyTableを実行し、更新された精度テーブルをプルリクエストに追加します。計画された問題を見てください。