091

[R] 머신러닝(1): 연관규칙분석-아프리오리 알고리즘 본문

Programming Language/R

[R] 머신러닝(1): 연관규칙분석-아프리오리 알고리즘

공구일 2026. 5. 23. 21:19
728x90

1. 연관규칙분석 이론

 

- 머신러닝이란 학습 데이터의 패턴을 "학습"한 후 새로운 데이터에 대해 정확한 추론을 할 수 있는 알고리즘에 초점을 맞춘 인공 지능(AI)의 하위 집합입니다.(출처: ibm think)

 

 

머신 러닝이란 무엇인가요? | IBM

머신 러닝은 새로운 데이터에 대한 정확한 추론을 하기 위해 학습 데이터의 패턴을 분석하고 "학습"하는 알고리즘에 초점을 맞춘 AI의 하위 집합입니다.

www.ibm.com

- 연관규칙분석이란 정답(Label)이 없는 데이터에서 항목 간의 숨겨진 패턴을 찾는 비지도 학습 중 일종입니다. 항목들 간의 조건->결과 패턴을 발견하는 기법이기도합니다.

-> [ A -> B ](만약 A를 샀다면 B도 산다)라는 구조에서 A(LHS, Left Hand Side)는 조건부, B(RHS,Right Hand Side)를 결과부라고 합니다. 

-> 연관 규칙의 3요소는 지지도(Support), 신뢰도(Confidence), 향상도(Lift)입니다.

요소 수식 의미
지지도(support) P(A∩B) = (A,B 함께 포함된 거래수) / (전체 거래수) 전체 거래 중 A와 B를 함께 산 비율
신뢰도(confidence) P(A∩B) / P(A) = (A,B 지지도) / (A 지지도) A를 샀을 때 B도 살 조건부 확률
향상도(lift) P(A∩B) / (P(A) × P(B))
= (A,B 지지도)/((A 지지도)*(B 지지도))
A와 B가 우연히 같이 나오는 것보다 얼마나 더 같이 나오는지에 대한 비율

 -> 향상도의 해석 기준은 1 이하 경우에는 의미 없는 규칙, 1.1 ~1.5면 약한 연관, 1.5~2.0은 유효환 규칙, 2.0 이상은 강한 연관입니다.

• 지지도 값 해석: 지지도({치킨}->{맥주})는 A,B 함께 포함된 거래수인 3을 분자로 전체 거래수 7을 분모로 둔 0.43이고, 그냥 지지도({치킨})은 A가 포함된 거래수인 6을 분자로 전체 거래수 7을 분모둔 0.86입니다.

• 신뢰도 값 해석: 신뢰도의 경우, A->B라는 언제나 조건부와 결과부가 같이 있을 때만 가질 수 있는 값입니다. 신뢰도({치킨}->{맥주})는 0.5, 신뢰도({맥주}->{치킨})는 1로 치킨을 산 경우 반 정도 맥주를 사지만, 맥주를 산 경우에는 반드시 치킨과 함께 산다는 의미입니다.

• 향상도 값 해석: 향상도 값은 위에서 말한 정확한 척도가 있으므로 향상도({치킨}->{맥주})는 1.2로 약한 연관을 가집니다. 

 

Q. 지지도나 신뢰도는 통일된 절대 기준이 없나요?

A. 지지도는 도메인(데이터의 성격)에 따라 다르게 적정 지지도를 지정해야합니다. 상품의 개수가 많은 경우에는 지지도를 0.5로 잡는 경우에는 거래의 절반에 나타나는 상품이 거의 없어 규칙이 나오지 않을 수 있기 때문에 적정 지지도를 데이터의 양에 따라 다르게 지정합니다. 신뢰도의 경우, 확률이라 상대적으로 기준이 명확합니다. 0.7 이상부터 신뢰할 만한 규칙이라고 판별하지만, 신뢰도가 높다고 무조건 연관성이 높다고 볼 수는 없습니다. 예를 들어 모든 사람이 B를 산다면, A->B 신뢰도가 1.0이 나오지만 이건 의미 없는 규칙입니다. 그래서 신뢰도와 향상도를 같이 봐줘야합니다.

 

- Apriori Algorithm은 1994년 Agrawal & Srikant 제안한 알고리즘으로 지지도를 활용해 자주 등장하는 항목 집합을 찾는 알고리즘입니다. 자주 안 나오는 항목이 포함된 집합은 자주 나올 수 없다는 핵심 원리를 활용해 최소 지지도 미만은 다음 단계로 넘기지 않습니다.

-> 아프리오리 알고리즘의 연관 규칙을 찾는 과정은 (1)빈도수 집합을 탐색하여, (2) 최소 지지도를 확인하고, (3) 후보 집합(빈발 itemset)을 생성한뒤 (2)~(3) 단계를 반복 수행하여 (5)연관 규칙을 수행합니다. 즉 위의 이미지처럼 빈발 itemset을 생성해낸 뒤, 신뢰도 높은 규칙을 생성하여, 향상도 등을 계산해 의미 있는 규칙만 이후에 남기게 됩니다.( 이 과정은 아래 코드 실행 단계에서 확인이 가능합니다.)

2. 연관규칙분석 R 실습

install.packages("arules")      # Apriori 알고리즘
install.packages("arulesViz")   # 연관규칙 시각화
library(arules)
library(arulesViz)

(1) 아프리오리 알고리즘을 사용과 그 연관규칙을 시각화하기 위해서 각각 필요한 패키지를 설치 및 가져와줍니다.

datest_min <- list(
  c("Pizza","Soda"),
  c("Pizza","Chicken"),
  c("Chicken","Soda"),
  c("Pizza","Soda")
)
datest_min

trans_min <- as(datest_min, "transactions")
trans_min
#transactions in sparse format with
#4 transactions (rows) and -> 행은 총 4개
#3 items (columns) -> 내부 데이터는 Pizza, Soda, Chicken으로 3개

(2.1) list를 transactions이라는 객체로 변환하여 사용하거는 방식입니다. transactions 객체는 거래인 행, 상품인 열, 포함여부(T/F)를 값으로 희소행렬(sparse matrix) 형태로 효율적으로 정합니다. 즉, 위의 그래프로 나타내면 이런식으로 내부 저장됩니다.

거래 Pizza Soda Chicken
1 1 1 0
2 1 0 1
3 0 1 1
4 1 1 0

 

trans <- read.transactions("ggi.csv", format="single", header=T,
                           cols=c(1,2),sep=",",rm.duplicates=T,
                           encoding="UTF-8")

transF <- read.transactions("ggi_F.csv",
                            format="single",header=T,
                            cols=c(2,3), sep=",", rm.duplicates=T,
                            encoding="UTF-8")
#read.transactions(file="파일경로", format="single"/"basket", header=첫 줄의 열 제목여부,
#		cols=(거래번호, 상품), sep="구분자", 
#		rm.duplicates=같은 거래 내 중복 상품 단일로 변경, encdoing="인코딩방식")

(2.2) read.transactions() 함수는 csv 데이터를 transactions 객체로 읽어올 때 사용합니다.

-> format이 single인 경우와 basket인 경우에는 아래와 같습니다.

"single" "basket"

-> rm.duplicates가 TRUE인 경우에는, 같은 열에 즉 맥주, 맥주, 땅콩 처럼 중복 상품이 있는 경우에는 맥주, 땅콩으로 처리합니다.

toLongFormat(trans_min)
toLongFormat(trans)
toLongFormat(transF)

(3) toFongFormat() 함수를 통해 transactions 객체를 (TID, item) 쌍의 긴 형식으로 변환하여 보기 쉽게 만들어줍니다.

trans_min trans gongguil.csv 파일 형태

 

#상품판매빈도 상위3개
itemFrequencyPlot(trans_min, topN=3, type="absolute", col="pink")

itemFrequencyPlot(trans, topN=3, type="absolute",xlab="상품명",
                  ylab="절대 판매빈도", main="판매량 많은 상품",col="skyblue")
#itemFrequencyPlot(x="transactions객체", topN=상위N개 상품 표시,
#                  type="absolute"/"relative", col="막대색상",
#                  xlab="x축라벨", ylab="y축 라벨", main ="그래프 제목")

(4) itemFrequencyPlot을 통해 상품판매 빈도 그래프를 확인할 수 있습니다.

-> type이 relative가 되면 말 그대로 비율로 옆의 y축이 변합니다. 지정하지만 기본값이 relative입니다.

y축을 지정하지 않으면, item frequency (absolute)라고 나옵니다.

rules_min <- apriori(trans_min, parameter=list(supp=0.4,conf=0.7))
#apriori(data=transactions 객체, 
#		parmeter=list(supp=최소지지도,conf=최소신뢰도,
#		minlen=최소item수,maxlen=최대item수,target="rules"/"frequent itemsets")							
inspect(rules_min)
#    lhs    rhs     support confidence coverage lift count
#[1] {}  => {Soda}  0.75    0.75       1        1    3    
#[2] {}  => {Pizza} 0.75    0.75       1        1    3    
plot(rules_min, method="graph")

(5.1) 위에서 데이터프레임->transactions 객체로 만들었던 trans_min의 아프리오리 알고리즘을 실행하여 결과를 rules_min에 넣어줬습니다. 

-> apriori 함수를 실행하면 verbose가 TRUE 상태여서 진행과정 로그를 화면에 출력하고 그게 바로 좌측 사진 속 로그들입니다. 우측에 있는 사진은 이후 apriori 함수가 뱉은 rules 객체인 rules_min을 method="graph" 즉, 노드-엣지 네트워크 그래프로 출력해준 것입니다.

-> rules_min의 coverage는 P(LHS)로 조건부의 지지도를 나타냅니다. 위의 예시의 경우 조건부가 없기 때문에 모든 거래가 이 규칙의 적용 대상으로 1입니다.

-> rules_min을 출력해보면 조건부 없이 결과부가 있는 두가지 규칙만이 출력됩니다. 그 이유는 최소 지지도인 0.4를 통과한 {Pizza, Soda}의 조합 {Pizza}->{Soda}와 {Soda}->{Pizza} 중 최소 신뢰도 0.7를 통과한 규칙이 없기 때문에 {}=>{Soda}, {}=>{Pizza}와 같은 1-itemset(원소 1개 집합)이 나온 것입니다. 그리고 이런 원소 1개 집합의 경우, 피자나 소다가 자주 팔린다는 통계 외에는 연관 규칙을 주지 못하기 때문에 만약 이런 1-itemset을 출력하고 싶지않다면 minlen=2로 설정하고 subset으로 필터링할 수 있습니다.

rules <- apriori(trans, parameter=list(supp=0.2, conf=0.7))
rules #set of 12 rules

inspect(rules[1:10])
#     lhs             rhs      support confidence coverage lift     count
#[1]  {오징어}     => {맥주}   0.2     1          0.2      1.666667 1    
#[2]  {라면}       => {땅콩}   0.2     1          0.2      2.500000 1    
#[3]  {라면}       => {맥주}   0.2     1          0.2      1.666667 1    
#[4]  {물}         => {껌}     0.2     1          0.2      2.500000 1    
#[5]  {물}         => {초콜렛} 0.2     1          0.2      2.500000 1    
#[6]  {땅콩}       => {맥주}   0.4     1          0.4      1.666667 2    
#[7]  {껌}         => {초콜렛} 0.4     1          0.4      2.500000 2    
#[8]  {초콜렛}     => {껌}     0.4     1          0.4      2.500000 2    
#[9]  {땅콩, 라면} => {맥주}   0.2     1          0.2      1.666667 1    
#[10] {라면, 맥주} => {땅콩}   0.2     1          0.2      2.500000 1    

plot(rules, method="graph")

(5.2) ggi.csv에서 읽어왔던 transactions 객체인 trans를 apriori 알고리즘을 실행한 결과를 rules에 담았습니다.

-> 이 노드-엣지 네트워크 그래프를 해석하는 방법으로는 먼저 이 그래프의 3요소를 이해하면 됩니다. 큰 글자 노드(단어)는 상품(Item), 작은 점(원)은 규칙(rule), 그리고 화살표는 규칙의 방향을 나타냅니다. 점의 크기는 지지도(support)를 점의 색깔은 향상도(lift)를 나타냈기 때문에 이미지에서 볼 수 있듯이 진한 빨간색+큰점을 가진 규칙이 자주 일어나고 연관성도 강한 규칙입니다.

rules.sort <- sort(rules, by='support', decreasing=T)
inspect(rules.sort[1:10])
#     lhs             rhs      support confidence coverage lift     count
#[1]  {땅콩}       => {맥주}   0.4     1          0.4      1.666667 2    
#[2]  {껌}         => {초콜렛} 0.4     1          0.4      2.500000 2    
#[3]  {초콜렛}     => {껌}     0.4     1          0.4      2.500000 2    
#[4]  {오징어}     => {맥주}   0.2     1          0.2      1.666667 1    
#[5]  {라면}       => {땅콩}   0.2     1          0.2      2.500000 1    
#[6]  {라면}       => {맥주}   0.2     1          0.2      1.666667 1    
#[7]  {물}         => {껌}     0.2     1          0.2      2.500000 1    
#[8]  {물}         => {초콜렛} 0.2     1          0.2      2.500000 1    
#[9]  {땅콩, 라면} => {맥주}   0.2     1          0.2      1.666667 1    
#[10] {라면, 맥주} => {땅콩}   0.2     1          0.2      2.500000 1

(6) 정렬은 support, confidence, lift, count 등 다양한 기준으로 할 수 있으면 decreasing은 내림차순 여부를 나타냅니다.

rules.lift2 <- subset(rules, subset=(lift>=2))
rules.lift2.sort <- sort(rules.lift2, by="lift", decreasing=T)
inspect(rules.lift2.sort[1:6])
#     lhs             rhs      support confidence coverage lift count
#[1] {라면}       => {땅콩}   0.2     1          0.2      2.5  1    
#[2] {물}         => {껌}     0.2     1          0.2      2.5  1    
#[3] {물}         => {초콜렛} 0.2     1          0.2      2.5  1    
#[4] {껌}         => {초콜렛} 0.4     1          0.4      2.5  2    
#[5] {초콜렛}     => {껌}     0.4     1          0.4      2.5  2    
#[6] {라면, 맥주} => {땅콩}   0.2     1          0.2      2.5  1

(7) subset을 통해 향상도 2 이상 값들만 필터링해주고, 위에서 사용한 sort 함수를 활용하여 정렬해줍니다.

rules_df <- as(rules.sort, "data.frame")
write.csv(rules_df, file="ggi_rules.csv",
          quote=T, row.names=F, fileEncoding="UTF-8")
#write.csv(데이터프레임, file="파일이름",
#          quote=문자열을 ""로 감쌀지 여부, row.names=행 번호 저장여부, fileEncoding="UTF-8"/"CP949")

(8) 데이터프레임으로 변환 후에 csv에 저장해줍니다.



+) Mac 한글 출력용 코드

install.packages("showtext")              # graph plot 한글용
library(showtext)
showtext_auto()
font_add("apple", "/System/Library/Fonts/Supplemental/AppleGothic.ttf")

-> 맥으로 출력 시에 노드-엣지 네트워크 그래프에서 par(family = "AppleGothic") 이전에 사용하던 이 설정이 적용되지 않기 때문에 위처럼 따로 지정해줘야합니다.

728x90