1. 데이터 불러오기
df <- read.delim(file.choose(), sep="\t", stringsAsFactors = FALSE)
2. 자질 매핑 (sonority, anteriority 등)
sonority_map <- c("sto"=1, "aff"=2, "fri"=3, "nas"=4, "liq"=5, "glide"=6, "vow"=7)
ant_map <- c("lab"=1, "den"=2, "alv"=3, "pal"=4, "vel"=5, "glo"=6)
height_map <- c("low"=1, "mid"=2, "hig"=3)
front_map <- c("bac"=1, "cen"=2, "fro"=3)
map_to_numeric <- function(col, map) {
as.numeric(map[as.character(col)])
}
df$son_o1 <- map_to_numeric(df$manner_o1, sonority_map)
df$son_c1 <- map_to_numeric(df$manner_c1, sonority_map)
df$son_o2 <- map_to_numeric(df$manner_o2, sonority_map)
df$son_c2 <- map_to_numeric(df$manner_c2, sonority_map)
df$ant_o1 <- map_to_numeric(df$place_o1, ant_map)
df$ant_c1 <- map_to_numeric(df$place_c1, ant_map)
df$ant_o2 <- map_to_numeric(df$place_o2, ant_map)
df$ant_c2 <- map_to_numeric(df$place_c2, ant_map)
df$hei1 <- map_to_numeric(df$height1, height_map)
df$hei2 <- map_to_numeric(df$height2, height_map)
df$fro1 <- map_to_numeric(df$front1, front_map)
df$fro2 <- map_to_numeric(df$front2, front_map)
df$gen <- factor(df$gen, levels = c(-1, 1), labels = c("female", "male"))
3. 자질 추가 (Feature Engineering)
#이름 길이 (음절 수)
df$name_len <- nchar(as.character(df$name))
#첫 음절이 비음인지(nasal- m,n)
df$nasal_start <- ifelse(df$manner_o1 == "nas", 1, 0)
#이름에 활음(glide - w,j)이 있는지
df$has_glide <- ifelse(df$manner_o1 == "glide" | df$manner_o2 == "glide", 1, 0)
head(df[, c("name", "name_len", "nasal_start", "has_glide")])
## name name_len nasal_start has_glide
## 1 민준 2 1 0
## 2 서준 2 0 0
## 3 예준 2 NA NA
## 4 도윤 2 0 NA
## 5 주원 2 0 NA
## 6 시우 2 0 NA
- 6명 모두 보편적인 이름인 2음절에 부합 (성 제외 2음절)
1. 민준 - 비음 시작, 활음 X = 전형적인 남자 이름
2. 서준 - 비음 X, 활음 X = 전형적인 남자 이름
3. 예준 - 비음 X, 활음 X (but, ’예’가 활음일 수도 O) = ’준’으로
끝나는 이름은 남자가 더 흔함
4. 도윤 - 비음 X, 활음 X (but, ’윤’이 활음일 수도 O) = 중성적이지만
남자이름이 더 많음
5. 주원 - 비음 X, 활음 X (but, ’원’이 활음일 수도 O) = 남자 이름으로
많이 알려져 있음
6. 시우 - 비음 X, 활음 X (but, ’우’가 활음일 수도 O) = 중성적
느낌
세가지 자질만으로는 성별예측 정확도를 알기 어려움
그러나, 이름 분포도에 남자이름이 더 많음을 알 수
있음.
향후 여자 이름 데이터를 추가하면 결과가 다를 것..?
자질 추가를 통해 분석할 수 있지만, 이름만으로 성별 예측은
완전하지 않음.
4. 학습/테스트 나누기 (sample() 사용)
numeric_vars <- c("son_o1", "son_c1", "son_o2", "son_c2",
"ant_o1", "ant_c1", "ant_o2", "ant_c2",
"hei1", "hei2", "fro1", "fro2",
"name_len", "nasal_start", "has_glide")
df[numeric_vars][is.na(df[numeric_vars])] <- 0
set.seed(123)
train_idx <- sample(nrow(df), 0.7 * nrow(df))
df.train <- df[train_idx, ]
df.test <- df[-train_idx, ]
# 클래스 분포 확인
table(df.test$gen)
##
## female male
## 98 82
5. 다른 모델과 AUC 비교
rf.model <- randomForest(gen ~ ., data = df.train[, c(numeric_vars, "gen")])
rf.prob <- predict(rf.model, newdata = df.test, type = "prob")[, "male"]
nb.model <- naiveBayes(gen ~ ., data = df.train[, c(numeric_vars, "gen")])
nb.prob <- predict(nb.model, newdata = df.test, type = "raw")[, "male"]
tree.model <- rpart(gen ~ ., data = df.train[, c(numeric_vars, "gen")])
tree.prob <- predict(tree.model, newdata = df.test)[, "male"]
rf.roc <- roc(df.test$gen, rf.prob)
## Setting levels: control = female, case = male
## Setting direction: controls < cases
nb.roc <- roc(df.test$gen, nb.prob)
## Setting levels: control = female, case = male
## Setting direction: controls < cases
tree.roc <- roc(df.test$gen, tree.prob)
## Setting levels: control = female, case = male
## Setting direction: controls < cases
plot(rf.roc, col="blue", main="ROC 비교")
lines(nb.roc, col="green")
lines(tree.roc, col="purple")
legend("bottomright", legend=c("Random Forest", "Naive Bayes", "Decision Tree"),
col=c("blue", "green", "purple"), lwd=2)

cat("RF AUC: ", auc(rf.roc), "\n")
## RF AUC: 0.8012693
cat("NB AUC: ", auc(nb.roc), "\n")
## NB AUC: 0.7098059
cat("Tree AUC: ", auc(tree.roc), "\n")
## Tree AUC: 0.6844201
랜덤 포레스트 모델이 가장 좋음을 알 수 있음 (면적이 가장 넓음)
랜덤포레스트: 0.78 (가장 효과적인 모델, 비선형적 or 복잡한 구조
파악할 수 있음 -> 비교적 정확도 높음)
나이브베이즈: 0.71 (복잡한 이름 구조 파악 어려움)
의사결정나무: 0.68 (과적합 가능성 있음, 정확도 낮음)
6. 전형적인 성별 이름 추출
rf.prob.all <- predict(rf.model, newdata=df[, numeric_vars], type="prob")
df$male_prob <- rf.prob.all[, "male"]
top_male <- head(df[order(-df$male_prob), c("name", "male_prob")], 5)
top_female <- head(df[order(df$male_prob), c("name", "male_prob")], 5)
top_male
## name male_prob
## 144 승훈 1.000
## 219 진혁 1.000
## 24 승현 0.998
## 152 성훈 0.998
## 220 승환 0.998
top_female
## name male_prob
## 366 나연 0.004
## 371 아린 0.010
## 394 예림 0.010
## 436 유리 0.020
## 507 보미 0.024