Yo Quero Language Detection - Part 1
One of the persistent issues we deal with at HumanGeo is determining the language of a block of text. There are multiple Language Detection (LD) libraries available claiming high accuracy, so building our own wasn’t necessary considering that these are libraries built by experts in Computational Linguistics. Out of the many choices, we were interested in determining the accuracy and performance of the different libraries for detecting the language of tweets. In the past, we have used the following libraries in various projects:
Recently, we needed to perform LD on text in Java, so we focused our efforts on two Java libraries, LangID and LanguageDetection. We ran tests on these two libraries to determine the accuracy and perfomance of the libraries. Below are the highlights of the process and a discussion of the results.
Process
To perform Testing on this classification problem, a prelabeled data set of text needs to be used for testing. For this, we use Twitter data that we gathered from the Twitter Streaming API.
Messages from Twitter contain a field for language (lang
) that has a two-letter ISO code representing the language of the tweet that Twitter has determined using their own process.
We understand this language classification by Twitter is not perfect, but
we will overlook this issue momentarily because the large quantities of categorized data available outweigh these issues.
We will not ignore this issue completely and even study the data Twitter provides at a later time.
Now that we have collected adequate data (millions), we take the text of every tweet and remove #hashtags, @mentions, and URLs.
Given that these elements have primarily English characters, even in tweets in other languages, we don’t want these elements to
throw off the language detection.
This filtered text is passed into the two Language Detectors to perform detection.
The two detectors each return a list of languages that are ranked from most likely to least likely.
The different libraries have a “threshold” value that can filter the returned languages that have a score/probability higher than the threshold value for convenience.
A quick note on probabilities
Many people request clarifications on what the “probablities” mean. Each language has a probability associated with it. For example, you could get the following as a list of languages for a detection: (en: .6, es: .3, fr: .1). These numbers mean English (en) is twice as likely as Spanish (es) to being the probable language English is also six times more likely than French (fr). Similarly, Spanish is three times as likely as French. The numbers are derived from true probabilities which end up being very small. They are scaled so that they maintain their relative proportions and they sum up to 1.
Comparison Code
The following is the high-level portion of code used the perform the main detection and store the results. This is where the actual detection happens. These blocks were derived from code found in tutorials and examples of the respective libraries.
The first portion just handles retrieving and stripping text from a Twitter message.
As stated, hashtags, mentions and urls are removed.
The lang
variable is the “true” language according to Twitter.
The detLang
is a variable for the detected Language from each library.
String text = massageMessage(message),
lang = getLanguage(message),
detLang;
The following block of code retrieves the top language from LanguageDetection’s detection.
If no language met its default threshold, the code assigns a value of und
.
The code then updates the confusion matrix with the true and detected languages.
List<DetectedLanguage> languageOpt =
languageDetector.getProbabilities(textObjectFactory.forText(text));
detLang = languageOpt.size() == 0 ? "und" : languageOpt.get(0).getLocale().getLanguage();
updateMatrix(detectorMatrix, lang, detLang);
The following block of code uses LangID to perform Language Detection similar to the block of code above. One difference here is that a bit of wrangling/sorting is needed to get the top detected language.
langID.classify(text, true);
List<DetectedLanguage> results = new ArrayList<>(langID.rank(true));
Collections.sort(results, (o1, o2) -> Float.compare(o2.confidence, o1.confidence));
List<String> detectedLangs = results.stream()
.map(DetectedLanguage::getLangCode)
.collect(Collectors.toList());
detLang = results.size() == 0 ? "und" : detectedLangs.get(0);
updateMatrix(idMatrix, lang, detLang);
Results - Precision, Recall, F1 Scores - Confusion Matrices
Below we present the F1, Recall, Precision scores and Total messages of each language. The most frequent language is English, with 1.4 million messages. Spanish, Portuguese, Japanese, Arabic, Indonesian, Turkish, Russian rounding out the top languages with at least 100k total messages.
There are a few differences in the results statistics between the two libraries.
LanguageDetection seems to return more und
values than LangID.
Having more undetermined identifications lowers the recall score of a category.
This is seen in several recall scores of LanguageDetection, in particular for English, with a recall score under .5.
In turn, by potentially removing ambiguities, a classifier may improve its precision.
Again, LanguageDetection has some precision scores above .9, as seen in English and Spanish.
LangID, on the other hand, was willing to make mistakes in the precision, but typically had reasonable recall.
Overall, which library someone would prefer “out of the box” depends on which metric is more important. Another way to see this: the classifier can either be: * very certain about it’s decision, while balking at any ambiguous text * ok with fielding a guess even though it may be wrong, with a focus on doing better at capturing certain languages.
LanguageDetection
lang | Lang | recall | precision | F1 | Total |
---|---|---|---|---|---|
am | Amharic | 0 | 0 | 0 | 6 |
ar | Arabic | 0.632 | 0.994 | 0.772703567 | 128462 |
bg | Bulgarian | 0.464 | 0.06 | 0.106259542 | 1010 |
bn | Bengali | 0.718 | 0.99 | 0.83234192 | 1274 |
ckb | null | 0 | 0 | 0 | 22 |
cs | Czech | 0.189 | 0.14 | 0.160851064 | 2386 |
cy | Welsh | 0.427 | 0.025 | 0.047234513 | 1808 |
da | Danish | 0.231 | 0.06 | 0.095257732 | 3577 |
de | German | 0.422 | 0.147 | 0.218045694 | 16680 |
el | “Greek Modern (1453-)” | 0.545 | 0.914 | 0.68283756 | 5124 |
en | English | 0.467 | 0.942 | 0.624434351 | 1402991 |
es | Spanish; Castilian | 0.37 | 0.957 | 0.533669932 | 482062 |
et | Estonian | 0.122 | 0.064 | 0.083956989 | 9448 |
eu | Basque | 0.353 | 0.091 | 0.144698198 | 2668 |
fa | Persian | 0.483 | 0.122 | 0.194796694 | 2288 |
fi | Finnish | 0.27 | 0.057 | 0.09412844 | 3148 |
fr | French | 0.456 | 0.679 | 0.545592952 | 82573 |
gu | Gujarati | 0.703 | 0.922 | 0.797742769 | 101 |
he | Hebrew | 0.725 | 0.986 | 0.83559322 | 1539 |
hi | Hindi | 0.139 | 0.908 | 0.241092646 | 7076 |
ht | Haitian; Haitian Creole | 0.16 | 0.022 | 0.038681319 | 6180 |
hu | Hungarian | 0.305 | 0.09 | 0.138987342 | 1624 |
hy | Armenian | 0 | 0 | 0 | 22 |
id | Indonesian | 0.258 | 0.7 | 0.377035491 | 126543 |
is | Icelandic | 0.272 | 0.138 | 0.183102439 | 1259 |
it | Italian | 0.458 | 0.326 | 0.380887755 | 31264 |
ja | Japanese | 0.89 | 0.999 | 0.941355214 | 213352 |
ka | Georgian | 0 | 0 | 0 | 14 |
km | Central Khmer | 0.96 | 1 | 0.979591837 | 25 |
kn | Kannada | 0.273 | 0.6 | 0.375257732 | 55 |
ko | Korean | 0.619 | 0.437 | 0.512316288 | 6776 |
lo | Lao | 0 | 0 | 0 | 5 |
lt | Lithuanian | 0.296 | 0.081 | 0.127193634 | 1477 |
lv | Latvian | 0.323 | 0.282 | 0.301110744 | 3248 |
ml | Malayalam | 0.441 | 1 | 0.612074948 | 111 |
mr | Marathi | 0.338 | 0.349 | 0.343411936 | 198 |
my | Burmese | 0 | 0 | 0 | 7 |
ne | Nepali | 0.399 | 0.848 | 0.542665597 | 1118 |
nl | Dutch; Flemish | 0.352 | 0.223 | 0.273029565 | 16497 |
no | Norwegian | 0.244 | 0.041 | 0.070203509 | 2838 |
or | Oriya | 0 | 0 | 0 | 12 |
pa | Panjabi; Punjabi | 0.2 | 1 | 0.333333333 | 25 |
pl | Polish | 0.424 | 0.445 | 0.43424626 | 11232 |
ps | Pushto; Pashto | 0 | 0 | 0 | 61 |
pt | Portuguese | 0.494 | 0.869 | 0.629913426 | 327847 |
ro | Romanian; Moldavian; Moldovan | 0.166 | 0.035 | 0.057810945 | 2640 |
ru | Russian | 0.483 | 0.962 | 0.643108651 | 104784 |
sd | Sindhi | 0 | 0 | 0 | 6 |
si | Sinhala; Sinhalese | 0 | 0 | 0 | 108 |
sl | Slovenian | 0.388 | 0.03 | 0.05569378 | 1305 |
sr | Serbian | 0.454 | 0.046 | 0.083536 | 679 |
sv | Swedish | 0.333 | 0.188 | 0.240322457 | 7545 |
ta | Tamil | 0.569 | 0.989 | 0.72238896 | 1405 |
te | Telugu | 0.565 | 0.929 | 0.702657296 | 23 |
th | Thai | 0.82 | 0.976 | 0.891224944 | 65742 |
tl | Tagalog | 0.518 | 0.574 | 0.544564103 | 72644 |
tr | Turkish | 0.491 | 0.937 | 0.644351541 | 131587 |
uk | Ukrainian | 0.251 | 0.481 | 0.32986612 | 10654 |
und | Undetermined | 0.818 | 0.147 | 0.249214508 | 269358 |
ur | Urdu | 0.38 | 0.371 | 0.375446072 | 2323 |
vi | Vietnamese | 0.078 | 0.046 | 0.057870968 | 2767 |
zh | Chinese | 0.387 | 0.54 | 0.450873786 | 3964 |
LangID
lang | Lang | recall | prec | F1 | Total |
---|---|---|---|---|---|
am | Amharic | 1 | 0.001 | 0.001998002 | 6 |
ar | Arabic | 0.837 | 0.952 | 0.890803801 | 127625 |
bg | Bulgarian | 0.535 | 0.067 | 0.119086379 | 949 |
bn | Bengali | 0.998 | 0.231 | 0.375163548 | 1262 |
ckb | null | 0 | 0 | 0 | 20 |
cs | Czech | 0.391 | 0.156 | 0.22302011 | 2532 |
cy | Welsh | 0.098 | 0.063 | 0.076695652 | 2202 |
da | Danish | 0.31 | 0.098 | 0.148921569 | 3717 |
de | German | 0.783 | 0.303 | 0.436922652 | 17057 |
el | “Greek Modern(1453-)” | 1 | 0.782 | 0.877665544 | 5119 |
en | English | 0.899 | 0.792 | 0.842114725 | 1472971 |
es | Spanish; Castilian | 0.853 | 0.907 | 0.879171591 | 548801 |
et | Estonian | 0.066 | 0.065 | 0.065496183 | 10684 |
eu | Basque | 0.132 | 0.066 | 0.088 | 3364 |
fa | Persian | 0.892 | 0.14 | 0.242015504 | 2259 |
fi | Finnish | 0.599 | 0.119 | 0.198554318 | 3250 |
fr | French | 0.832 | 0.696 | 0.757947644 | 86063 |
gu | Gujarati | 1 | 0.352 | 0.520710059 | 101 |
he | Hebrew | 0.992 | 0.145 | 0.253016711 | 1609 |
hi | Hindi | 0.518 | 0.695 | 0.59358615 | 7340 |
ht | Haitian; Haitian-Creole | 0.015 | 0.127 | 0.026830986 | 9366 |
hu | Hungarian | 0.413 | 0.118 | 0.183555556 | 1882 |
hy | Armenian | 0.826 | 0.024 | 0.046644706 | 23 |
id | Indonesian | 0.49 | 0.777 | 0.600994475 | 125555 |
is | Icelandic | 0.499 | 0.294 | 0.370002522 | 1461 |
it | Italian | 0.741 | 0.394 | 0.514456388 | 33668 |
ja | Japanese | 0.942 | 0.915 | 0.928303716 | 217173 |
ka | Georgian | 1 | 0.004 | 0.007968127 | 14 |
km | Central-Khmer | 0.96 | 0.002 | 0.003991684 | 25 |
kn | Kannada | 1 | 0.188 | 0.316498316 | 55 |
ko | Korean | 0.964 | 0.598 | 0.738120359 | 6959 |
lo | Lao | 1 | 0.005 | 0.009950249 | 5 |
lt | Lithuanian | 0.187 | 0.042 | 0.068593886 | 1848 |
lv | Latvian | 0.505 | 0.385 | 0.436910112 | 3653 |
ml | Malayalam | 1 | 0.227 | 0.37000815 | 111 |
mr | Marathi | 0.813 | 0.144 | 0.244664577 | 198 |
my | Burmese | 0 | 0 | 0 | 7 |
ne | Nepali | 0.743 | 0.465 | 0.572011589 | 1118 |
nl | Dutch;Flemish | 0.758 | 0.49 | 0.595224359 | 18726 |
no | Norwegian | 0.34 | 0.138 | 0.196317992 | 2666 |
or | Oriya | 1 | 0.05 | 0.095238095 | 12 |
pa | Panjabi;Punjabi | 1 | 0.174 | 0.296422487 | 25 |
pl | Polish | 0.758 | 0.51 | 0.609747634 | 12075 |
ps | Pushto;Pashto | 0.59 | 0.02 | 0.038688525 | 61 |
pt | Portuguese | 0.774 | 0.921 | 0.841125664 | 354859 |
ro | Romanian; Moldavian; Moldovan | 0.124 | 0.048 | 0.069209302 | 2622 |
ru | Russian | 0.834 | 0.945 | 0.886037099 | 101405 |
sd | Sindhi | 0 | 0 | 0 | 5 |
si | Sinhala;Sinhalese | 1 | 0.076 | 0.141263941 | 122 |
sl | Slovenian | 0.475 | 0.074 | 0.128051002 | 1247 |
sr | Serbian | 0.766 | 0.051 | 0.095632803 | 552 |
sv | Swedish | 0.703 | 0.355 | 0.471767486 | 7750 |
ta | Tamil | 0.996 | 0.647 | 0.784433354 | 1405 |
te | Telugu | 1 | 0.113 | 0.203054807 | 23 |
th | Thai | 0.999 | 0.827 | 0.904899233 | 65745 |
tl | Tagalog | 0.398 | 0.731 | 0.515390611 | 82385 |
tr | Turkish | 0.883 | 0.971 | 0.924911543 | 124692 |
uk | Ukrainian | 0.541 | 0.486 | 0.512027264 | 10490 |
und | null | 0 | 0 | 0 | 292757 |
ur | Urdu | 0.92 | 0.174 | 0.292650823 | 2325 |
vi | Vietnamese | 0.099 | 0.073 | 0.084034884 | 3096 |
zh | Chinese | 0.986 | 0.074 | 0.137667925 | 4191 |
Confusion Matrices
Below are the visual Confusion Matrices that represent the categorization totals of each language. The rows are labeled with the “truth” language, while the columns are labeled with detected language. Each cell in the matrix is color and transparency coded to represent the relative weight to the other cells in the row. The values of the cells are not linear (.1 value -> .9 transparency) but log scaled to bring out low scores and visualize any potential clusters.
The diagonals are labeled as blue because in this view with logarithmic scales, it would not be appropriate to compare and contrast the values of correct detection to the incorrect detections. That type of analysis is meant to be done using the recall/precision as shown above. The matrix is meant to determine what other languages are being detected instead of the true language.
The confusion matrices are available for download as csv files: LangID, LangDetection
The frequency ranking in the dropdown sorts according to the number of “true” texts for each row.
As stated before, the rows get sorted to English, Spanish, Portuguese, Japanese, Arabic, Indonesian, Turkish, Russian.
Additionally there is the und
for the “true” language that represents messages that haven’t been identified by Twitter.
Some interesting false detections points:
- When Arabic messages get detected incorrectly, they are usually tagged: Pashto, Urdu, Farsi
- For Japanese messages, Chinese is the culprit
- For English: French, German, Spanish, Italian, and Chinese(??)
- For Russian: Bulgarian, Serbian, Ukranian
- For Korean: Japanese, Thai
LanguageDetection Confusion Matrix
LangID Confusion Matrix
Concluding Remarks
This study was an overview of a few Java-based Language Detection libraries. Though there is no clear indication of a “better” library, our preference is to use LangID “out of the box”, because it has a reasonable recall score for many languages.
We will delve into other issues such as the true accuracy of Twitter’s language detection in the future. This will help us create a proper Gold-Standard test (and maybe training) set for future studies. This study is meant to reiterate the fact that no machine learning classifier is perfect. It is also a helpful push to anyone interested in this problem and looking to contribute, since the source code to these libraries are on github.com and available for modification.
Additional Notes:
Twitter is still tagging Messages as in
for Indonesian, and iw
for Hebrew.
This has been reported to Twitter.