Sua tarefa é simples: diz em qual idioma alguns dados textuais fornecidos são escritos. Isso é muito útil como uma etapa de pré -processamento para dados lingüísticos em aplicativos de processamento de linguagem natural, como classificação de texto e verificação de ortografia. Outros casos de uso, por exemplo, podem incluir o roteamento de e-mails para o departamento de atendimento ao cliente localizado geograficamente, com base nos e-mails dos idiomas.
A detecção de idiomas geralmente é feita como parte de grandes estruturas de aprendizado de máquina ou aplicativos de processamento de linguagem natural. Nos casos em que você não precisa da funcionalidade completa desses sistemas ou não deseja aprender as cordas dessas, uma pequena biblioteca flexível é útil.
Até agora, três outras bibliotecas abrangentes de código aberto que trabalham na JVM para esta tarefa são o Apache Tika, o Apache OpenNLP e o OptimAize Language Detector. Infelizmente, especialmente o último tem três grandes desvantagens:
A Lingua pretende eliminar esses problemas. Quase não precisa de nenhuma configuração e gera resultados bastante precisos em texto longo e curto, mesmo em palavras e frases únicas. Ele se baseia nos métodos baseados em regras e estatísticos, mas não usa nenhum dicionário de palavras. Também não precisa de uma conexão com nenhuma API ou serviço externo. Depois que a biblioteca for baixada, ela pode ser usada completamente offline.
Comparado a outras bibliotecas de detecção de idiomas, o foco da Lingua está na qualidade da quantidade , ou seja, obtendo a detecção correta para um pequeno conjunto de idiomas antes de adicionar novos. Atualmente, os 75 idiomas a seguir são suportados:
A Lingua é capaz de relatar estatísticas de precisão para alguns dados de teste em pacote disponíveis para cada idioma suportado. Os dados do teste para cada idioma são divididos em três partes:
Os modelos de idiomas e os dados de teste foram criados a partir de documentos separados do Wortschatz Corpora oferecido pela Universidade de Leipzig, Alemanha. Os dados se arrastaram em vários sites de notícias foram usados para treinamento, cada corpus compreendendo um milhão de sentenças. Para testes, os corpora feitos de sites escolhidos arbitrariamente foram usados, cada um compreendendo dez mil frases. De cada corpus de teste, foi extraído um subconjunto aleatório de 1000 palavras, 1000 pares de palavras e 1000 frases, respectivamente.
Dados os dados de teste gerados, comparei os resultados de detecção de Lingua , Apache Tika , Apache OpenNLP e OptimAize Language Detector usando testes JUNIT parametrizados que executam os dados dos 75 idiomas suportados pela Lingua . Os idiomas que não são suportados pelas outras bibliotecas são simplesmente ignorados para aqueles durante o processo de detecção.
Cada uma das seções a seguir contém duas parcelas. O gráfico de barras mostra os resultados detalhados da precisão para cada linguagem suportada. O gráfico da caixa ilustra as distribuições dos valores de precisão para cada classificador. As próprias caixas representam as áreas em que os 50 % do meio dos dados estão dentro. Dentro das caixas coloridas, as linhas horizontais marcam a mediana das distribuições.
A tabela abaixo mostra estatísticas detalhadas para cada idioma e classificador, incluindo média, mediana e desvio padrão.
| Linguagem | Média | Palavras únicas | Pares de palavras | Frases | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Língua (Modo de alta precisão) | Língua (Modo de baixa precisão) | Tika | Opennlp | Optimaize | Língua (Modo de alta precisão) | Língua (Modo de baixa precisão) | Tika | Opennlp | Optimaize | Língua (Modo de alta precisão) | Língua (Modo de baixa precisão) | Tika | Opennlp | Optimaize | Língua (Modo de alta precisão) | Língua (Modo de baixa precisão) | Tika | Opennlp | Optimaize | |
| afrikaans | 79 | 64 | 71 | 72 | 39 | 58 | 38 | 44 | 41 | 3 | 81 | 62 | 70 | 75 | 22 | 97 | 93 | 98 | 99 | 93 |
| albanês | 88 | 80 | 79 | 71 | 70 | 69 | 54 | 54 | 40 | 38 | 95 | 86 | 84 | 73 | 73 | 100 | 99 | 99 | 100 | 98 |
| árabe | 98 | 94 | 97 | 84 | 89 | 96 | 88 | 94 | 65 | 72 | 99 | 96 | 99 | 88 | 94 | 100 | 99 | 100 | 99 | 100 |
| Armênio | 100 | 100 | - | 100 | - | 100 | 100 | - | 100 | - | 100 | 100 | - | 100 | - | 100 | 100 | - | 100 | - |
| Azerbaijão | 90 | 82 | - | 82 | - | 77 | 71 | - | 60 | - | 92 | 78 | - | 86 | - | 99 | 96 | - | 99 | - |
| Basco | 84 | 74 | 83 | 77 | 66 | 71 | 56 | 64 | 56 | 33 | 87 | 76 | 86 | 82 | 70 | 93 | 91 | 98 | 92 | 95 |
| Bielorrusso | 97 | 92 | 96 | 91 | 87 | 92 | 80 | 92 | 78 | 69 | 99 | 95 | 98 | 95 | 92 | 100 | 100 | 100 | 100 | 99 |
| bengali | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| Bokmal | 58 | 49 | - | 66 | - | 39 | 27 | - | 42 | - | 59 | 47 | - | 69 | - | 75 | 74 | - | 87 | - |
| Bósnia | 35 | 29 | - | 26 | - | 29 | 23 | - | 12 | - | 35 | 29 | - | 22 | - | 40 | 36 | - | 44 | - |
| búlgaro | 87 | 78 | 73 | 83 | 48 | 70 | 56 | 52 | 62 | 18 | 91 | 81 | 69 | 87 | 36 | 99 | 96 | 96 | 100 | 91 |
| catalão | 70 | 58 | 58 | 42 | 31 | 51 | 33 | 32 | 11 | 2 | 74 | 60 | 57 | 32 | 16 | 86 | 81 | 84 | 81 | 77 |
| chinês | 100 | 100 | 69 | 78 | 31 | 100 | 100 | 20 | 40 | 0 | 100 | 100 | 86 | 94 | 2 | 100 | 100 | 100 | 100 | 91 |
| croata | 72 | 60 | 74 | 50 | 41 | 53 | 36 | 54 | 23 | 8 | 74 | 57 | 72 | 44 | 24 | 90 | 85 | 97 | 81 | 91 |
| Tcheco | 80 | 71 | 72 | 67 | 49 | 66 | 54 | 54 | 42 | 21 | 84 | 72 | 75 | 70 | 46 | 91 | 87 | 88 | 90 | 81 |
| dinamarquês | 81 | 70 | 83 | 60 | 55 | 61 | 45 | 63 | 34 | 19 | 84 | 70 | 86 | 52 | 51 | 98 | 95 | 99 | 94 | 96 |
| Holandês | 77 | 64 | 60 | 61 | 39 | 55 | 36 | 31 | 31 | 6 | 81 | 61 | 52 | 57 | 19 | 96 | 94 | 98 | 97 | 91 |
| Inglês | 81 | 62 | 64 | 52 | 41 | 55 | 29 | 30 | 10 | 2 | 89 | 62 | 62 | 46 | 23 | 99 | 96 | 99 | 99 | 97 |
| esperanto | 84 | 66 | - | 76 | - | 67 | 44 | - | 50 | - | 85 | 61 | - | 78 | - | 98 | 92 | - | 100 | - |
| estoniano | 92 | 83 | 84 | 59 | 61 | 80 | 62 | 66 | 29 | 23 | 96 | 88 | 88 | 60 | 63 | 100 | 99 | 100 | 88 | 98 |
| finlandês | 96 | 91 | 94 | 86 | 79 | 90 | 77 | 86 | 68 | 51 | 98 | 95 | 96 | 91 | 86 | 100 | 100 | 100 | 100 | 100 |
| Francês | 89 | 77 | 78 | 59 | 54 | 74 | 52 | 55 | 25 | 18 | 94 | 83 | 80 | 55 | 48 | 99 | 97 | 99 | 98 | 97 |
| Ganda | 91 | 84 | - | - | - | 79 | 65 | - | - | - | 95 | 87 | - | - | - | 100 | 100 | - | - | - |
| Georgiano | 100 | 100 | - | 100 | - | 100 | 100 | - | 100 | - | 100 | 100 | - | 100 | - | 100 | 100 | - | 100 | - |
| Alemão | 89 | 80 | 74 | 67 | 55 | 74 | 57 | 50 | 38 | 21 | 94 | 84 | 71 | 66 | 46 | 100 | 99 | 100 | 98 | 99 |
| grego | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| Gujarati | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| hebraico | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| hindi | 73 | 33 | 80 | 58 | 51 | 61 | 11 | 65 | 28 | 16 | 64 | 20 | 75 | 49 | 38 | 93 | 67 | 99 | 99 | 98 |
| húngaro | 95 | 90 | 88 | 78 | 77 | 87 | 77 | 75 | 53 | 51 | 98 | 94 | 91 | 82 | 82 | 100 | 100 | 100 | 100 | 99 |
| islandês | 93 | 88 | 90 | 76 | 78 | 83 | 72 | 76 | 53 | 53 | 97 | 92 | 94 | 76 | 82 | 100 | 99 | 100 | 99 | 99 |
| indonésio | 60 | 48 | 60 | 29 | 18 | 39 | 25 | 37 | 10 | 0 | 61 | 46 | 62 | 25 | 1 | 81 | 72 | 82 | 52 | 54 |
| irlandês | 91 | 85 | 90 | 78 | 80 | 82 | 70 | 80 | 56 | 58 | 94 | 90 | 92 | 82 | 85 | 96 | 95 | 99 | 97 | 98 |
| italiano | 87 | 71 | 80 | 64 | 51 | 69 | 42 | 58 | 31 | 12 | 92 | 74 | 84 | 61 | 43 | 100 | 98 | 99 | 100 | 98 |
| japonês | 100 | 100 | 25 | 95 | 98 | 100 | 100 | 1 | 87 | 99 | 100 | 100 | 5 | 100 | 100 | 100 | 100 | 68 | 100 | 96 |
| Cazaque | 92 | 90 | - | 85 | - | 80 | 78 | - | 66 | - | 96 | 93 | - | 90 | - | 99 | 99 | - | 100 | - |
| coreano | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| Latim | 87 | 73 | - | 70 | - | 72 | 49 | - | 43 | - | 93 | 76 | - | 71 | - | 97 | 93 | - | 96 | - |
| letão | 93 | 87 | 90 | 86 | 78 | 85 | 75 | 78 | 72 | 56 | 97 | 90 | 93 | 88 | 82 | 99 | 97 | 98 | 98 | 97 |
| lituano | 95 | 87 | 89 | 79 | 72 | 86 | 76 | 74 | 56 | 40 | 98 | 89 | 92 | 83 | 77 | 100 | 98 | 99 | 99 | 98 |
| Macedônio | 84 | 72 | 83 | 68 | 46 | 66 | 52 | 66 | 37 | 10 | 86 | 70 | 83 | 68 | 32 | 99 | 95 | 100 | 98 | 97 |
| malaio | 31 | 31 | 23 | 19 | 4 | 26 | 22 | 19 | 10 | 0 | 38 | 36 | 22 | 20 | 0 | 30 | 36 | 28 | 27 | 11 |
| maori | 92 | 83 | - | 92 | - | 84 | 64 | - | 85 | - | 92 | 88 | - | 90 | - | 99 | 98 | - | 100 | - |
| Marathi | 85 | 41 | 90 | 81 | 71 | 74 | 20 | 81 | 62 | 43 | 85 | 30 | 92 | 83 | 74 | 96 | 72 | 98 | 98 | 96 |
| mongol | 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 | - |
| persa | 90 | 80 | 81 | 75 | 62 | 78 | 62 | 65 | 53 | 29 | 94 | 80 | 79 | 74 | 58 | 100 | 98 | 99 | 99 | 99 |
| polonês | 95 | 90 | 90 | 83 | 81 | 85 | 77 | 76 | 61 | 57 | 98 | 93 | 93 | 89 | 86 | 100 | 99 | 100 | 100 | 100 |
| Português | 81 | 69 | 63 | 58 | 40 | 59 | 42 | 34 | 22 | 7 | 85 | 70 | 58 | 54 | 19 | 98 | 95 | 98 | 98 | 94 |
| Punjabi | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| romeno | 87 | 72 | 78 | 67 | 55 | 69 | 49 | 57 | 34 | 24 | 92 | 74 | 80 | 68 | 50 | 99 | 94 | 97 | 99 | 91 |
| russo | 90 | 78 | 80 | 50 | 53 | 76 | 59 | 62 | 20 | 22 | 95 | 84 | 85 | 43 | 50 | 98 | 92 | 94 | 86 | 87 |
| sérvio | 88 | 78 | 73 | 73 | 46 | 74 | 62 | 57 | 46 | 18 | 90 | 80 | 70 | 74 | 39 | 99 | 91 | 90 | 98 | 80 |
| Shona | 91 | 81 | - | - | - | 78 | 56 | - | - | - | 96 | 86 | - | - | - | 100 | 100 | - | - | - |
| Eslovaco | 84 | 75 | 76 | 70 | 47 | 64 | 49 | 53 | 39 | 12 | 90 | 78 | 76 | 73 | 38 | 99 | 97 | 98 | 99 | 92 |
| Esloveno | 82 | 67 | 74 | 71 | 37 | 61 | 39 | 53 | 43 | 3 | 87 | 68 | 72 | 72 | 18 | 99 | 93 | 98 | 99 | 90 |
| Somali | 92 | 85 | 91 | 69 | 79 | 82 | 64 | 78 | 35 | 50 | 96 | 90 | 94 | 74 | 88 | 100 | 100 | 100 | 98 | 100 |
| Sotho | 85 | 72 | - | - | - | 67 | 43 | - | - | - | 90 | 75 | - | - | - | 99 | 97 | - | - | - |
| Espanhol | 70 | 56 | 59 | 42 | 32 | 44 | 26 | 29 | 8 | 0 | 69 | 49 | 50 | 25 | 6 | 97 | 94 | 97 | 93 | 91 |
| Suaíli | 81 | 70 | 75 | 73 | 60 | 60 | 43 | 50 | 45 | 26 | 84 | 68 | 75 | 74 | 58 | 98 | 97 | 99 | 99 | 98 |
| sueco | 84 | 72 | 71 | 69 | 50 | 64 | 46 | 44 | 41 | 15 | 88 | 76 | 72 | 69 | 42 | 99 | 95 | 97 | 97 | 94 |
| Tagalog | 78 | 66 | 77 | 61 | 61 | 52 | 36 | 53 | 27 | 23 | 83 | 67 | 79 | 57 | 62 | 99 | 96 | 99 | 98 | 97 |
| tâmil | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| Telugu | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| Tailandês | 99 | 99 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 98 | 98 | 100 | 99 | 100 |
| Tsonga | 84 | 72 | - | - | - | 66 | 46 | - | - | - | 89 | 73 | - | - | - | 98 | 97 | - | - | - |
| Tswana | 84 | 71 | - | - | - | 65 | 44 | - | - | - | 88 | 73 | - | - | - | 99 | 96 | - | - | - |
| turco | 94 | 87 | 81 | 72 | 70 | 84 | 71 | 62 | 48 | 43 | 98 | 91 | 83 | 71 | 70 | 100 | 99 | 99 | 98 | 96 |
| ucraniano | 92 | 86 | 81 | 79 | 68 | 84 | 75 | 62 | 54 | 39 | 97 | 92 | 84 | 83 | 69 | 95 | 93 | 97 | 99 | 94 |
| urdu | 91 | 80 | 83 | 68 | 72 | 80 | 65 | 68 | 45 | 49 | 94 | 78 | 84 | 62 | 71 | 98 | 96 | 96 | 98 | 96 |
| vietnamita | 91 | 87 | 85 | 84 | 87 | 79 | 76 | 63 | 66 | 65 | 94 | 87 | 92 | 86 | 95 | 99 | 98 | 100 | 100 | 100 |
| galês | 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 | - | - | - |
| Ioruba | 75 | 62 | - | - | - | 50 | 33 | - | - | - | 77 | 61 | - | - | - | 97 | 93 | - | - | - |
| zulu | 81 | 70 | - | 78 | - | 62 | 45 | - | 51 | - | 83 | 72 | - | 82 | - | 97 | 94 | - | 100 | - |
| Significar | 86 | 77 | 80 | 74 | 65 | 74 | 61 | 64 | 53 | 41 | 89 | 78 | 81 | 74 | 61 | 96 | 93 | 96 | 95 | 93 |
| Mediana | 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 |
| Desvio padrão | 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 |
Todo detector de idiomas usa um modelo probabilístico de N-Gram treinado na distribuição de caracteres em algum corpus de treinamento. A maioria das bibliotecas usa apenas n-gramas de tamanho 3 (trigramas), o que é satisfatório para detectar a linguagem de fragmentos de texto mais longos que consistem em múltiplas frases. Para frases curtas ou palavras únicas, no entanto, os trigramas não são suficientes. Quanto mais curto o texto de entrada, menos n-gramas estiverem disponíveis. As probabilidades estimadas a partir de poucos gramas N não são confiáveis. É por isso que a Lingua faz uso de n-gramas de tamanhos 1 até 5, o que resulta em uma previsão muito mais precisa do idioma correto.
Uma segunda diferença importante é que a Lingua não usa apenas um modelo estatístico, mas também um mecanismo baseado em regras. Este mecanismo primeiro determina o alfabeto do texto de entrada e procura caracteres que sejam exclusivos em um ou mais idiomas. Se exatamente um idioma puder ser escolhido de maneira confiável dessa maneira, o modelo estatístico não será mais necessário. De qualquer forma, o mecanismo baseado em regras filtra idiomas que não satisfazem as condições do texto de entrada. Somente então, em um segundo passo, o modelo probabilístico de N-Gram é levado em consideração. Isso faz sentido porque carregar menos modelos de linguagem significa menos consumo de memória e melhor desempenho de tempo de execução.
Em geral, é sempre uma boa idéia restringir o conjunto de idiomas a serem considerados no processo de classificação usando os respectivos métodos da API. Se você souber de antemão que certos idiomas nunca ocorrem em um texto de entrada, não deixe que eles participem do processo de classificação. O mecanismo de filtragem do mecanismo baseado em regras é bastante bom, no entanto, a filtragem com base no seu próprio conhecimento do texto de entrada é sempre preferível.
Se você deseja reproduzir os resultados da precisão acima, pode gerar os relatórios de teste para todos os quatro classificadores e todos os idiomas fazendo:
./gradlew accuracyReport
Você também pode restringir os classificadores e idiomas para gerar relatórios, passando argumentos para a tarefa Gradle. A tarefa a seguir gera relatórios para a língua e apenas para os idiomas em inglês e alemão:
./gradlew accuracyReport -Pdetectors=Lingua -Planguages=English,German
Por padrão, apenas um único núcleo de CPU é usado para geração de relatórios. Se você possui uma CPU com vários núcleos em sua máquina, poderá garra tantos processos quanto os núcleos da CPU. Isso acelera significativamente a geração de relatórios. No entanto, esteja ciente de que a forquilha mais de um processo pode consumir muita RAM. Você faz assim:
./gradlew accuracyReport -PcpuCores=2
Para cada detector e idioma, um arquivo de relatório de teste é então gravado em /accuracy-reports , a ser encontrado ao lado do diretório src . Como exemplo, aqui está a saída atual do relatório da Lingua German:
##### 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%
A língua é hospedada em pacotes Github e 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 usa o gradle para construir e requer java> = 1,8 para isso.
git clone https://github.com/pemistahl/lingua.git
cd lingua
./gradlew build
Vários arquivos JAR podem ser criados a partir do projeto.
./gradlew jar reúne lingua-1.2.2.jar contendo apenas as fontes compiladas../gradlew sourcesJar reúne lingua-1.2.2-sources.jar CONTENDO O CÓDIGO PRIMEIRO PRIMEIRO../gradlew jarWithDependencies monta lingua-1.2.2-with-dependencies.jar contendo as fontes compiladas e todas as dependências externas necessárias no tempo de execução. Este arquivo JAR pode ser incluído em projetos sem sistemas de gerenciamento de dependência. Também pode ser usado para executar a língua no modo independente (veja abaixo). A lingua pode ser usada programaticamente em seu próprio código ou no modo independente.
A API é bem direta e pode ser usada no código Kotlin e 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 " ) A API pública de Lingua nunca retorna null em algum lugar, por isso é seguro ser usado no código Java também.
/* 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" );Por padrão, a Lingua retorna o idioma mais provável para um determinado texto de entrada. No entanto, existem certas palavras que são as mesmas em mais de um idioma. A palavra prólogo , por exemplo, é uma palavra válida em inglês e francês. A LINGINE geraria inglês ou francês, o que pode estar errado no contexto especificado. Para casos como esse, é possível especificar uma distância relativa mínima que as probabilidades logaritmizadas e resumidas para cada idioma possível precisam satisfazer. Pode ser declarado da seguinte maneira:
val detector = LanguageDetectorBuilder
.fromAllLanguages()
.withMinimumRelativeDistance( 0.25 ) // minimum: 0.00 maximum: 0.99 default: 0.00
.build() Esteja ciente de que a distância entre as probabilidades do idioma depende do comprimento do texto de entrada. Quanto mais tempo o texto de entrada, maior a distância entre os idiomas. Portanto, se você deseja classificar frases de texto muito curtas, não defina a distância relativa mínima muito alta. Caso contrário, você obterá a maioria dos resultados retornados como Language.UNKNOWN , que é o valor de retorno para os casos em que a detecção de idiomas não é possível de maneira confiável.
Saber sobre o idioma mais provável é bom, mas quão confiável é a probabilidade calculada? E quão menos prováveis são os outros idiomas examinados em comparação com o mais provável? Essas perguntas também podem ser respondidas:
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
// }No exemplo acima, um mapa de todos os idiomas possíveis é retornado, classificado por seu valor de confiança na ordem descendente. Os valores que o detector calcula fazem parte de uma métrica de confiança relativa , não de uma absoluta. Cada valor é um número entre 0,0 e 1,0. A linguagem mais provável é sempre retornada com o valor 1.0. Todos os outros idiomas obtêm valores atribuídos que são inferiores a 1,0, denotando quão menos prováveis esses idiomas são em comparação com o idioma mais provável.
O mapa retornado por esse método não contém necessariamente todos os idiomas dos quais a instância de chamada do LanguageDetector foi construída. Se o mecanismo baseado em regras decidir que um idioma específico é realmente impossível, não fará parte do mapa retornado. Da mesma forma, se nenhuma probabilidade NGRAM puder ser encontrada dentro dos idiomas do detector para o texto de entrada fornecido, o mapa retornado estará vazio. O valor de confiança para cada idioma que não faz parte do mapa retornado é assumido como 0,0.
Por padrão, a Lingua usa o carregamento preguiçoso para carregar apenas os modelos de idiomas sob demanda que são considerados relevantes pelo mecanismo de filtro baseado em regras. Para serviços da Web, por exemplo, é bastante benéfico pré -carregar todos os modelos de idiomas na memória para evitar latência inesperada enquanto aguarda a resposta do serviço. Se você deseja ativar o modo de carregamento ansioso, pode fazer assim:
LanguageDetectorBuilder .fromAllLanguages().withPreloadedLanguageModels().build() Múltiplas instâncias do LanguageDetector compartilham os mesmos modelos de idiomas na memória que são acessados de forma assíncrona pelas instâncias.
A alta precisão de detecção da Lingua tem o custo de ser notavelmente mais lento do que outros detectores de idiomas. Os grandes modelos de linguagem também consomem quantidades significativas de memória. Esses requisitos podem não ser viáveis para os sistemas com poucos recursos. Se você deseja classificar principalmente textos longos ou precisar economizar recursos, pode permitir um modo de baixa precisão que carregue apenas um pequeno subconjunto dos modelos de idiomas na memória:
LanguageDetectorBuilder .fromAllLanguages().withLowAccuracyMode().build()A desvantagem dessa abordagem é que a precisão da detecção para textos curtos que consistem em menos de 120 caracteres cairão significativamente. No entanto, a precisão da detecção para textos com mais de 120 caracteres permanecerá principalmente não afetada.
Uma alternativa para uma pegada de memória menor e um desempenho mais rápido é reduzir o conjunto de idiomas ao criar o detector de idiomas. Na maioria dos casos, não é aconselhável construir o detector a partir de todos os idiomas suportados. Quando você tem conhecimento sobre os textos que deseja classificar, quase sempre pode descartar certos idiomas como impossíveis ou improváveis.
Pode haver tarefas de classificação em que você sabe com antecedência que os dados do seu idioma definitivamente não estão escritos em latim, por exemplo (que surpresa :-). A precisão da detecção pode se tornar melhor nesses casos se você excluir determinados idiomas do processo de decisão ou apenas incluir explicitamente idiomas relevantes:
// 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 )Internamente, a Lingua usa com eficiência todos os núcleos da sua CPU para acelerar o carregamento dos modelos e a detecção de idiomas. Para esse fim, é usado um forkjoinpool interno. Se a biblioteca for usada em um servidor de aplicativos, a memória consumida não será liberada automaticamente quando o aplicativo não for implantado.
Se você deseja liberar todos os recursos da Lingua , precisará fazer isso manualmente ligando para detector.unloadLanguageModels() durante o não implantação. Isso limpará todos os modelos de linguagem carregados da memória, mas o pool de threads continuará em execução.
Se você deseja experimentar a Lingua antes de decidir se deve usá -la ou não, poderá executá -la em um REPL e ver imediatamente seus resultados de detecção.
./gradlew runLinguaOnConsole --console=plainjava -jar lingua-1.2.2-with-dependencies.jarEntão basta brincar:
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!
Caso você queira contribuir com algo para a Lingua , é incentivado a fazê -lo. Você tem idéias para melhorar a API? Existem alguns idiomas específicos que você deseja suportar mais cedo? Ou você encontrou algum bug até agora? Sinta -se à vontade para abrir um problema ou enviar uma solicitação de tração. É muito apreciado.
Para solicitações de tração, verifique se todos os testes de unidade passam e se o código é formatado de acordo com o guia oficial do estilo Kotlin. Você pode verificar isso executando o Kotlin Linter Ktlint usando ./gradlew ktlintCheck . A maioria dos problemas que o Linnter identifica pode ser corrigida executando ./gradlew ktlintFormat . Todos os outros problemas, especialmente linhas com mais de 120 caracteres, não podem ser corrigidos automaticamente. Nesse caso, formate as respectivas linhas manualmente. Você notará que a compilação falhará se a formatação não estiver correta.
Todos os tipos de solicitações de tração são bem -vindos. Os mais favoritos são novas adições de idiomas. Se você deseja contribuir com novos idiomas para a Lingua , aqui vem um manual detalhado, explicando como fazer isso.
Muito obrigado antecipadamente por todas as contribuições, por mais pequenas que sejam.
Para executar as etapas abaixo, você precisará de Java 8 ou mais. Embora a própria biblioteca seja executada em java> = 6, as classes FilesWriter usam a API java.nio, que foi introduzida com o Java 8.
IsoCode639_1 e IsoCode639_3 e adicione os códigos ISO da linguagem. Entre outros sites, a Wikipedia fornece uma lista abrangente.Language enum e adicione uma nova entrada para o seu idioma. Se o idioma for escrito com um script que ainda não é suportado pelo enum Alphabet da Lingua , adicione uma nova entrada lá também.Language enum. No entanto, se os caracteres ocorrerem em mais de um idioma , mas não em todos os idiomas, adicione -os à constante Constant de CHARS_TO_LANGUAGES_MAPPING .LanguageModelFilesWriter para criar os arquivos do modelo de idioma. O arquivo de dados de treinamento usado para a estimativa de probabilidade do Ngram não é necessário para ter um formato específico que não seja um arquivo TXT válido./src/main/resources/language-models e coloque os arquivos de modelo de linguagem gerados lá. Não renomeie os arquivos do modelo de idioma. O nome do subdiretório deve ser o código ISO 639-1 do idioma, completamente mais baixo.TestDataFilesWriter para criar os arquivos de dados de teste usados para geração de relatórios de precisão. O arquivo de entrada para criar os dados de teste deve ter cada frase em uma linha separada./src/accuracyReport/resources/language-testdata . Não renomeie os arquivos de dados de teste./src/accuracyReport/kotlin/com/github/pemistahl/lingua/report/config . Veja os arquivos dos outros idiomas neste diretório para ver como a classe deve ser. Deve ser bastante auto-explicativo./src/accuracyReport/kotlin/com/github/pemistahl/lingua/report/lingua . Veja os arquivos dos outros idiomas neste diretório para ver como a classe deve ser. Deve ser bastante auto-explicativo. Se uma das outras bibliotecas de detector de idiomas já suportar seu idioma, você também poderá adicionar classes de teste para elas. Cada biblioteca tem seu próprio diretório para esse fim. Se o seu idioma não for suportado pelas outras bibliotecas de detectores de idiomas, exclua -o no AbstractLanguageDetectionAccuracyReport .linguaSupportedLanguages em /gradle.properties ../gradlew accuracyReport e adicione os relatórios de precisão atualizados à sua solicitação de tração../gradlew drawAccuracyPlots e adicione os gráficos atualizados à sua solicitação de tração../gradlew writeAccuracyTable e adicione a tabela de precisão atualizada à sua solicitação de tração.Dê uma olhada nos problemas planejados.