Dansk og naiv machine learning
Alle, der har lært dansk, ved, at der er to grammatiske køn i sproget: fælleskøn (eller n-ord) og intetkøn (eller t-ord).
Ifølge Wikipedia:
Historisk set havde navneord på dansk og svensk, ligesom andre germanske sprog, et af tre grammatiske køn: maskulinum, femininum eller neutrum. Med tiden smeltede femininum og maskulinum sammen til fælleskøn.
De fleste danske navneord er n-ord, og der er ingen regler for, hvordan man skelner mellem n- og t-ord. Lærere anbefaler som regel bare at huske dem.
På den anden side siger folk normalt om et nyt ord, de hører, at det lyder som et n- eller t-ord.
Hvis man siger, at et ord lyder som et t-ord, kunne det så være, at vores intuition er i stand til at opbygge nogle regler til at løse dette spørgsmål? Jeg har altid været interesseret i at finde svar på det.
Det simpleste, jeg kunne gøre, var at bede en computer om at klassificere de danske navneord og finde regler til at skelne mellem n- og t-ord.
Jeg tog en XML-fil med et stort sæt danske ord fra Wiktionary dumps (tak til Steve for at pege mig i den retning) og brugte et R-script til at behandle og klassificere dataene.
library(xml2)
xml <- read_xml("./data/dawiktionary-latest-pages-meta-current.xml")
nouns <- data.frame(
xml_text(xml_find_all(xml, "//d1:page//d1:title", xml_ns(xml))),
xml_text(xml_find_all(xml, "//d1:page//d1:text", xml_ns(xml))))
colnames(nouns) <- c("title", "text")
Det giver os en data frame med 48008 rækker. Lad os fjerne alt undtagen navneord fra vores data frame:
nouns <- nouns[!grepl(":", nouns$title), ]
nouns <- nouns[grepl("\\{\\{-noun-\\|da\\}\\}", nouns$text), ]
nrow(nouns) # 8004
Nu har vi 8004 navneord. Lad os tilføje en kolonne et, som skal indeholde booleske værdier: et for t-ord og en for n-ord:
nouns$et <- apply(nouns, 1, function(x) {
ifelse(
grepl(paste("et\\|", x[1], sep = ""), x[2], ignore.case = TRUE),
"et",
"en")
})
nouns$et <- factor(nouns$et)
nouns$text <- NULL
nrow(nouns[nouns$et == "et",]) # 1552
nrow(nouns[nouns$et != "et",]) # 6452
Næste skridt er at tilføje flere kolonner med attributter, så computeren kan klassificere ordene efter disse attributter. Lad os starte med ordets længde og bygge et decision tree baseret på denne attribut:
library(party)
nouns$letters_count <- apply(nouns, 1, function(x) nchar(x[1]))
tree <- ctree(et ~ letters_count, data = nouns)
plot(tree)
Som vi kan se på grafen, er der lidt under 20% t-ord blandt ord med en længde på mere end 8 tegn, og lidt over 20% t-ord blandt ord med en længde på 8 tegn eller derunder. Indtil videre ser det ikke ud som et effektivt regelsæt, da andelen af t-ord i vores data frame er ca. 19%. Men lad os skrive en funktion, der anvender vores sæt af navneord på decision tree'et og tæller, hvor mange gange det giver os det rigtige svar om, hvorvidt det er et n- eller t-ord:
test_tree <- function(tree, nouns) {
prediction <- data.frame(nouns$et, predict(tree, nouns))
colnames(prediction) <- c("fact", "prediction")
prediction$valid <- apply(prediction, 1, function(x) ifelse(x[1] == x[2], TRUE, FALSE))
nrow(prediction[prediction$valid,])
}
test_tree(tree, nouns) # 6452
Der er 6452 rigtige svar, hvilket svarer til antallet af n-ord i vores nouns data frame. Vores decision tree er altså ikke særlig effektivt.
Hvad nu hvis navneordets køn korrelerer med de første eller sidste bogstaver i ordet? Lad os tilføje flere attributter for at undersøge det. Der er 29 bogstaver i det danske alfabet, så der vil være mindst 29 booleske attributter for hvert første bogstav, mindst 29 for hvert sidste bogstav. Efter samme princip tilføjer vi attributter for de første to og sidste to bogstaver i hvert ord (29 * 29 = 841).
# Dansk ABC
abc <- c(
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
"K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z", "Æ", "Ø", "Å")
columns <- c("letters_count")
for (letter1 in abc) {
letter <- letter1
column_name <- paste("first_letter_is_", letter, sep="")
column <- apply(nouns, 1, function(row) ifelse(toupper(substring(row[1], 1, 1)) == letter, TRUE, FALSE))
if (length(column[column==TRUE]) > 0) {
nouns[column_name] <- column
columns <- append(columns, column_name)
for (letter2 in abc) {
letter <- paste(letter1, letter2, sep="")
column_name <- paste("first_letter_is_", letter, sep="")
column <- apply(
nouns, 1, function(row) ifelse(
toupper(substring(row[1], 1, 2)) == letter,
TRUE, FALSE))
if (length(column[column==TRUE]) > 0) {
nouns[column_name] <- column
columns <- append(columns, column_name)
}
}
}
}
for (letter1 in abc) {
letter <- letter1
column_name <- paste("last_letter_is_", letter, sep="")
column <- apply(
nouns, 1, function(row) ifelse(
toupper(substring(row[1], nchar(row[1]), nchar(row[1]))) == letter1,
TRUE, FALSE))
if (length(column[column==TRUE]) > 0) {
nouns[column_name] <- column
columns <- append(columns, column_name)
for (letter2 in abc) {
letter <- paste(letter2, letter1, sep="")
column_name <- paste("last_letters_are_", letter, sep="")
column <- apply(
nouns, 1, function(row) ifelse(
toupper(substring(row[1], nchar(row[1]) - 1, nchar(row[1]))) == letter,
TRUE, FALSE))
if (length(column[column==TRUE]) > 0) {
nouns[column_name] <- column
columns <- append(columns, column_name)
}
}
}
}
tree <- ctree(as.formula(paste("et ~ ", paste(columns, collapse = "+"))), data = nouns)
plot(tree)
test_tree(tree, nouns) # 6595
Dette tree giver os 6595 rigtige svar, hvilket kun er 143 svar mere, men stadig ikke nok til at stole på det. Til gengæld kan vi se, at der er nogle tegn, vi kan bruge, og som vores hjerne måske bruger, når vi fornemmer, at et ord lyder som et n- eller t-ord.
For eksempel:
- Ud af 133 ord der ender på "um" er der 109 t-ord (82%). For eksempel: et amfibium, et faktotum, et punktum, men en rosarium.
- Ud af 1650 ord der ender på "e" er der kun 110 t-ord (7%). For eksempel: en næse, en pige, men et æble.
Konklusion: desværre er det nemmere bare at huske alle ordene. Selvom der er nogle regler, der kan hjælpe os med at gætte det rigtige grammatiske køn, er de mere komplekse end blot et sæt af første og sidste bogstaver.