‘비지도 학습은’ 8장의 지도 학습과는 달리 응답변수 Y가 없는 학습
기법이다.
단순히 관측치인 X집합만 가지고 있으며, 그들 사이의 관계를 이해하고자
한다.
: 개체에 대한 설명이 숫자변수로 구성된 경우
분류에서 Tree를 구성하기 위한 두 단계:
변수마다 다른 단위에 있을 경우, 분류에 반영하기 어렵다. 변수의 크기를
조정할 필요가 생기는데, 이 경우 도메인 지식에 따라 최적의 방법을
결정해야 한다. 계층적 군집화에선 간단히 dist()함수를 사용할 수 있다.
dist()함수는 각 개별 entity에서 모든 entities까지의 거리를 제공한다.
거리는 전체 트리가 구성될 때 까지 가까운 항목 사이에 분기를 구성한 다음
분기 간에 연결하는데 사용할 수 있고, 이를 계층적 군집화라고 한다.
: 계층 구조의 구성없이, 각 사례를 여러 그룹 중 하나에 할당하는 것을
말한다.
EX: WorldCities 데이터를 사용한다. 4,000개 대도시의 위도와 경도
정보는 있지만 대륙에 대한 정보는 없는 상황이다.
위치만을 기준으로 k개 군집으로 분류해보고, 대륙이 옳게 분류되었는지
알아보자.
BigCities <- world_cities %>%
arrange(desc(population)) %>%
head(4000) %>%
select(longitude, latitude)
glimpse(BigCities)
Rows: 4,000
Columns: 2
$ longitude <dbl> 121.45806, 28.94966, -58.37723, 72.88261, -99.12766, 116.39723, 67.01040, 117.17667, 113.25000, 7…
$ latitude <dbl> 31.22222, 41.01384, -34.61315, 19.07283, 19.42847, 39.90750, 24.86080, 39.14222, 23.11667, 28.651…
인구 수로 정렬한 상위 4,000개 대도시의 위도와 경도 정보를 살펴볼 수
있다.
set.seed(15)
city_clusts <- BigCities %>%
kmeans(centers = 6) %>%
fitted("classes") %>%
as.character()
BigCities <- BigCities %>% mutate(cluster = city_clusts)
BigCities %>% ggplot(aes(x = longitude, y = latitude)) +
geom_point(aes(color = cluster), alpha = 0.5)
위치만을 기준으로 하여 k=6, 즉 6개 군집으로 분류해본 결과이다. 실제와
거의 유사하게 분류한 것을 확인할 수 있다.
: 종종 데이터에 구하고자 하는 task에 도움되는 정보가 없는 변수, 또는
유사한 패턴을 가지거나 동일한 정보의 변수가 존재할 수 있다. 이들은
알고리즘의 학습을 방해하는 노이즈로 취급되므로 제거해 줄 필요가
있다.
EX: 2008년 스코틀랜드 의회의 투표 데이터
찬/반 투표 패턴을 통해 투표인의 소속 정당을 클러스터링 할 수 있는지 알아보자.
Votes %>%
mutate(Vote = factor(vote, labels = c("Nay","Abstain","Aye"))) %>%
ggplot(aes(x = bill, y = name, fill = Vote)) +
geom_tile() + xlab("Ballot") + ylab("Member of Parliament") +
scale_fill_manual(values = c("darkgray", "white", "goldenrod")) +
scale_x_discrete(breaks = NULL, labels = NULL) +
scale_y_discrete(breaks = NULL, labels = NULL)
마치 “스코티쉬 타탄” 패턴이 보이는 듯 하지만, 많은 패턴을 식별하긴
아직 어렵다.
스코티쉬 타탄이란 다음과 같은 문양을 말한다.
Votes %>% filter(bill %in% c("S1M-240.2", "S1M-639.1")) %>%
tidyr::spread(key = bill, value = vote) %>%
ggplot(aes(x = `S1M-240.2`, y = `S1M-639.1`)) +
geom_point(alpha = 0.7, position = position_jitter(width = 0.1, height = 0.1)) +
geom_point(alpha = 0.01, size = 10, color = "red" )
위 결과는 SIM-240.2와 SIM-639.1 두 투표에 대한 결과를 보여준다.
결과는 (예X아니오X기권)으로 3X3=9, 9가지 가능한 경우가 있고, 현재
8그룹이 나타나있다. 본 결과는 과연 8개의 군집을 의미할까?
Votes %>% mutate(set_num = as.numeric(factor(bill)),
set =
ifelse(set_num < max(set_num) / 2, "First_Half", "Second_Half")) %>%
group_by(name, set) %>%
summarize(Ayes = sum(vote)) %>%
tidyr::spread(key = set, value = Ayes) %>%
ggplot(aes(x = First_Half, y = Second_Half)) +
geom_point(alpha = 0.7, size = 5)
두 개의 투표 결과가 아니라 모든 투표 데이터를 사용하니, 구성원이 두
군집으로 나타날 수 있음을 보였다.
앞서 n=134 명의 p=773회 투표데이터 시각화에서 x에 투표 전반부, y에 후반부를 지정하였는데, 이보다 더 나은 지정 방법이 존재할 수도 있다. SVD는 이러한 투표-유권자 간 최상의 근사치를 행렬을 통해 찾을 수 있는 수학적 접근 방식이다. SVD는 좌표축의 회전을 의미하므로 주성분 몇 가지로 많은 변동을 설명할 수 있다.
Votes_wide <- Votes %>%
pivot_wider(names_from = bill, values_from = vote)
vote_svd <- Votes_wide %>%
select(-name) %>%
svd()
num_clusters <- 5
vote_svd_tidy <- vote_svd %>%
tidy(matrix = "u") %>%
filter(PC < num_clusters) %>%
mutate(PC = paste0("pc_", PC)) %>%
pivot_wider(names_from = PC, values_from = value) %>%
select(-row)
New names:
clusts <- vote_svd_tidy %>%
kmeans(centers = num_clusters)
tidy(clusts)
voters <- clusts %>%
augment(vote_svd_tidy)
ggplot(data = voters, aes(x = pc_1, y = pc_2)) +
geom_point(aes(x = 0, y = 0), color = "red", shape = 1, size = 7) +
geom_point(size = 5, alpha = 0.6, aes(color = .cluster)) +
xlab("Best Vector from SVD") +
ylab("Second Best Vector from SVD") +
ggtitle("Political Positions of Members of Parliament") +
scale_color_brewer(palette = "Set2")
※Clustering members of Scottish Parliament based on SVD along
the members
5개의 클러스터를 입력하였으나, 최상의 SVD합계를 통해 분할된 군집은
3개로 보인다. Confusion Matrix를 통해 실제 정당이 얼마나 잘 분류되었는지
확인해볼 수 있다.
voters <- voters %>%
mutate(name = Votes_wide$name) %>%
left_join(Parties, by = c("name" = "name"))
mosaic::tally(party ~ .cluster, data = voters)
.cluster
party 1 2 3 4 5
Member for Falkirk West 0 1 0 0 0
Scottish Conservative and Unionist Party 0 0 0 20 0
Scottish Green Party 0 1 0 0 0
Scottish Labour 0 1 0 0 57
Scottish Liberal Democrats 0 16 0 0 1
Scottish National Party 26 0 10 0 0
Scottish Socialist Party 0 1 0 0 0
ballots <- vote_svd %>%
tidy(matrix = "v") %>%
filter(PC < num_clusters) %>%
mutate(PC = paste0("pc_", PC)) %>%
pivot_wider(names_from = PC, values_from = value) %>%
select(-column)
New names:
clust_ballots <- kmeans(ballots, centers = num_clusters)
ballots <- clust_ballots %>%
augment(ballots) %>%
mutate(bill = names(select(Votes_wide, -name)))
ggplot(data = ballots, aes(x = pc_1, y = pc_2)) +
geom_point(aes(x = 0, y = 0), color = "red", shape = 1, size = 7) +
geom_point(size = 5, alpha = 0.6, aes(color = .cluster)) +
xlab("Best Vector from SVD") +
ylab("Second Best Vector from SVD") +
ggtitle("Influential Ballots") +
scale_color_brewer(palette = "Set2")
※Clustering of Scottish Parliament ballots based on SVD along
the ballots
위의 클러스터링 된 이미지에선 군집이 중앙의 빨간 점을 기준으로
대칭되어 사회적 진보대 보수, 경제적 진보 대 보수로 해석될 수도 있다.
투표 데이터에서 정치적 군집 외에도 사회적 효과, 경제적 효과 등 얻을 수
있는 정보가 더 존재하고, 주성분은 투표-유권자를 재배치하는 데 사용할 수
있다.
Votes_svd <- Votes %>%
mutate(Vote = factor(vote, labels = c("Nay", "Abstain", "Aye"))) %>%
inner_join(ballots, by = "bill") %>%
inner_join(voters, by = "name")
ggplot(data = Votes_svd,
aes(x = reorder(bill, pc_1.x), y = reorder(name, pc_1.y), fill = Vote)) +
geom_tile() +
xlab("Ballot") +
ylab("Member of Parliament") +
scale_fill_manual(values = c("darkgray", "white", "goldenrod")) +
scale_x_discrete(breaks = NULL, labels = NULL) +
scale_y_discrete(breaks = NULL, labels = NULL)
데이터가 상하로 나뉘었는데, 상단을 보면 두 개의 주요 정당으로
데이터가 명확하게 분류된 것을 볼 수 있다. 배경을 고려하면 국민당과
노동당이 구분된 것으로 유추할 수 있고, 나머지 작은 정당들은 하단의
절반에서 덜 명확하게 분류되고 있다.
기계학습이 데이터에서 의미있는 패턴을 식별하였지만, 추출한
패턴을 해석하기 위해선 인간의 도메인 지식을 문제에 적용하는 것이
중요하다.