본문 바로가기
AI/머신러닝

15. 연관 규칙 학습 | Apriori , Eclat

by 사라리24 2024. 10. 7.



1. 연관 규칙 학습 (Association Rule Learning)이란?

데이터셋 내의 항목들 간에 발생하는 연관성을 찾는 비지도 학습 기법입니다.
주로 '장바구니 분석(Market Basket Analysis)'에 사용되며,
고객이 함께 구매하는 제품들 간의 관계를 발견하거나,
어떤 항목들이 함께 자주 등장하는지를 파악하는 데 활용됩니다.

 

1. 연관 규칙 학습의 주요 개념

  • 항목집합(Itemset): 하나 이상의 항목으로 구성된 집합입니다.
    예를 들어, A와 B라는 제품이 있을 때, {A}, {B}, {A, B}가 항목집합이 됩니다.

  • 연관 규칙(Association Rule):
    연관 규칙은 "만약 A를 구매하면 B를 구매할 확률이 높다"와 같은 형태로,
    특정 항목(A)이 발생했을 때 다른 항목(B)이 발생할 가능성을 설명하는 규칙입니다. 보통 A → B로 표현됩니다.

  • 지지도(Support): 전체 거래 중 특정 항목집합이 등장한 비율을 나타냅니다.
    지지도는 해당 규칙이 얼마나 자주 등장하는지를 측정합니다.
    예를 들어, A와 B가 함께 등장한 비율을 구하는 것과 같습니다.
  • 신뢰도(Confidence): A가 발생한 거래 중 B가 함께 발생한 거래의 비율입니다.
    즉, A를 구매한 고객이 B도 구매할 확률을 나타냅니다.

  • 향상도(Lift): B가 단독으로 등장하는 비율 대비 A가 등장할 때 B가 등장할 가능성의 비율입니다.
    향상도가 1보다 크면, A와 B가 함께 나타날 가능성이 평균보다 높다는 뜻입니다.

2. 활용

    • 장바구니 분석: 슈퍼마켓에서 고객이 자주 함께 구매하는 상품들을 파악하여 추천 상품을 제안하거나, 상품 배치를 최적화하는 데 사용됩니다. 예를 들어, "고객이 빵을 구매하면 버터도 자주 구매한다"는 규칙을 발견할 수 있습니다.
    • 의료 데이터 분석: 특정 질병이나 증상, 약물 간의 관계를 파악하여 진단 및 치료에 도움을 줄 수 있습니다.
    • 웹 사용 분석: 사용자가 웹사이트에서 자주 클릭하는 항목들을 분석해, 사용자 행동을 예측하거나 개인화된 콘텐츠를 제공할 수 있습니다.

3. Apriori 알고리즘

    • Apriori 알고리즘은 연관 규칙 학습에서 가장 많이 사용되는 알고리즘 중 하나로, 대규모 데이터셋에서 빈번한 항목 집합(frequent itemsets)을 찾아내기 위해 설계되었습니다. Apriori 알고리즘은 아래와 같은 특징과 과정으로 설명할 수 있습니다:
      • 빈발 항목 집합(Frequent Itemset): 일정 수준 이상의 빈도로 함께 발생하는 항목들의 집합입니다. 예를 들어, 장바구니 분석에서 자주 함께 구매되는 상품들(빵과 버터)이 빈발 항목 집합이 될 수 있습니다.
      • Apriori 속성(Apriori Property): 빈발 항목 집합의 모든 부분 집합도 빈발 항목 집합이어야 한다는 성질을 이용합니다. 즉, 만약 {A, B}가 빈발 항목 집합이면, {A}와 {B}도 빈발 항목 집합이 됩니다.
      작동 원리
      1. 단일 항목집합의 지지도 계산: 모든 항목에 대해 지지도(Support)를 계산하고, 최소 지지도를 넘는 항목만 선택합니다.
      2. 부분 집합 생성: 빈발 항목 집합을 기반으로 두 개 이상의 항목을 결합하여 더 큰 항목 집합을 생성합니다. 이때, Apriori 속성을 사용하여 지지도가 낮은 항목 집합은 더 이상 확장하지 않습니다.
      3. 지지도 계산: 새로 생성된 항목 집합에 대해 지지도를 다시 계산하고, 최소 지지도를 넘는 항목들만 남깁니다.
      4. 연관 규칙 생성: 최종적으로 연관 규칙을 생성하여 신뢰도(Confidence)나 향상도(Lift)를 측정합니다. 이를 통해 유의미한 규칙을 도출할 수 있습니다.
      장점
      • Apriori 속성을 활용하여 검색 공간을 크게 줄임으로써 연관 규칙 학습의 효율성을 높일 수 있습니다.
      단점
      • 데이터셋이 클 경우 계산 비용이 많이 듭니다. 항목 집합의 크기가 커질수록 계산해야 하는 경우의 수가 기하급수적으로 증가합니다.
      예시
    • 장바구니 분석에서, 고객이 빵을 구매하면 버터도 구매할 확률이 높은 규칙 빵 → 버터를 찾을 수 있습니다.

4. Eclat 알고리즘

  • Eclat(Eclat: Equivalence Class Clustering and bottom-up Lattice Traversal) 알고리즘은 Apriori 알고리즘과 비슷한 목적을 가진 알고리즘이지만, 집합 간의 교집합을 사용해 빈발 항목 집합을 찾는 방식이 다릅니다. Eclat 알고리즘은 트랜잭션을 아이템의 출현 목록으로 변환하여 효율적으로 탐색합니다.
    • 빈발 항목 집합 탐색: Eclat는 트랜잭션 항목들 간의 교집합을 통해 빈발 항목 집합을 찾습니다. 즉, 항목이 어떤 트랜잭션에서 나타나는지를 트랜잭션 ID 목록(TID)으로 변환해, 항목 집합 간의 교집합을 계산하여 빈발 항목 집합을 도출합니다.
    작동 원리
    1. 트랜잭션 ID(TID) 집합 생성: 각 항목마다 트랜잭션 ID 목록을 생성합니다. 이 목록은 해당 항목이 포함된 트랜잭션들의 ID를 나타냅니다.
    2. 교집합 계산: 빈발 항목 집합을 만들기 위해 항목 집합 간의 교집합을 구합니다. 두 개의 항목이 포함된 트랜잭션 목록의 교집합을 구하고, 이 교집합의 크기를 통해 해당 항목 집합의 지지도를 계산합니다.
    3. 재귀적 확장: 교집합을 기반으로 더 큰 빈발 항목 집합을 재귀적으로 확장해 나갑니다. 교집합을 계산할 때마다 지지도가 기준보다 높으면 항목 집합을 확장합니다.
    장점
    • 트랜잭션 ID 기반으로 항목 집합을 처리하므로, 메모리 효율적이며 탐색 속도가 빠릅니다.
    • 데이터셋의 구조에 따라 Apriori보다 더 빠르게 빈발 항목 집합을 찾을 수 있습니다.
    단점
    • 데이터셋이 희소하지 않고, 교집합을 계산하는 항목이 많으면 메모리 사용량이 많아질 수 있습니다.
    • 수직 탐색을 사용하기 때문에 수평적인 구조로 데이터를 처리하는 경우 메모리 부족 문제를 겪을 수 있습니다.
    예시
  • 트랜잭션 ID 집합을 기반으로, 빵과 버터가 동시에 등장한 트랜잭션들을 추적하여 해당 항목 집합이 빈발 항목인지 확인할 수 있습니다.

5. Apriori와 Eclat 비교



두 알고리즘 모두 연관 규칙 학습을 위한 강력한 도구이지만,

데이터셋의 크기와 특성에 따라 적합한 알고리즘을 선택하는 것이 중요합니다.
Apriori는 단계적으로 데이터를 확장하고, Eclat는 수직적 탐색을 통해 빠르게 빈발 항목을 찾아냅니다.

 

 

2. Apriori 실습

 

 

장바구니 데이터 

 

 

apyori 설치하기


       
        !pip install apyori


Collecting apyori
  Downloading apyori-1.1.2.tar.gz (8.6 kB)
  Preparing metadata (setup.py) ... done
Building wheels for collected packages: apyori
  Building wheel for apyori (setup.py) ... done
  Created wheel for apyori: filename=apyori-1.1.2-py3-none-any.whl size=5953 sha256=721937003013c3b16093bab54666b6f0d5bf8816f30a62006ca0b98dea6e55dd
  Stored in directory: /root/.cache/pip/wheels/c4/1a/79/20f55c470a50bb3702a8cb7c94d8ada15573538c7f4baebe2d
Successfully built apyori
Installing collected packages: apyori
Successfully installed apyori-1.1.2

 

 

 

import 


       
        import numpy as np
        import matplotlib.pyplot as plt
        import pandas as pd

 
 

 

 

데이터 전처리해서 가져오기


      
        dataset = pd.read_csv('데이터 위치', header = None)
        transactions = []
        for i in range(0, 7501):
          transactions.append([str(dataset.values[i,j]) for j in range(0, 20)])


  • for i in range(0, 7501)::
    • 반복문으로 데이터셋의 각 행을 순차적으로 처리합니다.
    • 7501은 데이터셋에 포함된 거래의 총 개수로, 데이터가 7501행(트랜잭션) 있다는 가정하에 작성된 코드입니다.
      각 반복에서 '하나의 트랜잭션(거래)'을 처리합니다.
  • transactions.append([str(dataset.values[i,j]) for j in range(0, 20)]):
    • dataset.values[i,j]는 데이터셋에서 i번째 행(트랜잭션)의 j번째 열에 있는 값을 가져옵니다.
    • for j in range(0, 20)는 0번 열부터 19번 열까지 (총 20개의 열)을 순차적으로 가져옵니다.
      즉, 각 트랜잭션에 대해 20개의 항목을 읽습니다.
    • str() 함수는 각 항목을 문자열로 변환합니다. 대부분의 연관 규칙 알고리즘에서는 항목들이 문자열로 처리됩니다.
    • 이 과정을 통해 하나의 트랜잭션에서 20개의 아이템을 가져와서 리스트로 만들고, transactions 리스트에 추가합니다.

 

 

dataset 확인


     

      dataset


 

 

모든 거래 리스트 보기


       
        transactions

 
[['shrimp',
  'almonds',
  'avocado',
  'vegetables mix',
  'green grapes',
  'whole weat flour',
  'yams',
  'cottage cheese',
  'energy drink',
  'tomato juice',
  'low fat yogurt',
  'green tea',
  'honey',
  'salad',
  'mineral water',
  'salmon',
  'antioxydant juice',
  'frozen smoothie',
  'spinach',
  'olive oil'],
 ['burgers',
  'meatballs',
  'eggs',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan'],
 ['chutney',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan',
  'nan'],
....

 

 

데이터셋에서 Apriori 모델을 학습하기


       
        from apyori import apriori
        rules = apriori(transactions = transactions, min_support = 0.003, min_confidence = 0.2, min_lift = 3, min_length = 2, max_length = 2)


  • from apyori import apriori:
    • apyori라는 패키지에서 apriori 함수를 임포트합니다. apyori는 파이썬에서 Apriori 알고리즘을 구현한 라이브러리로, 연관 규칙 학습을 간편하게 수행할 수 있도록 도와줍니다.
  • rules = apriori(...):
    • apriori 함수를 호출하여 연관 규칙을 생성하고, 결과를 rules 변수에 저장합니다. 이 변수는 생성된 규칙들의 리스트를 포함하게 됩니다.
  • 매개변수 설명:
    • transactions = transactions:
      • 분석할 데이터셋으로, 이전에 생성된 transactions 리스트를 사용합니다. 이 리스트는 각 트랜잭션이 포함된 항목들의 리스트로 구성되어 있습니다.
    • min_support = 0.003:
      • 지지도(Support)의 최소 기준값입니다. 규칙을 유효한 것으로 간주하기 위해, 전체 트랜잭션 수 중에서 해당 항목 집합이 0.3% 이상 발생해야 한다는 의미입니다. 즉, 빈발 항목 집합의 지지도가 0.003 이상인 경우에만 고려됩니다.
    • min_confidence = 0.2:
      • 신뢰도(Confidence)의 최소 기준값입니다. A가 발생했을 때 B가 발생할 확률을 나타내며, 이 값이 0.2 이상인 규칙만 생성됩니다. 즉, "고객이 A를 구매하면 B도 구매할 확률이 20% 이상일 때" 규칙이 유효하다고 판단됩니다.
    • min_lift = 3:
      • 향상도(Lift)의 최소 기준값입니다. 향상도는 두 항목 간의 연관성을 평가하는 지표로, 값이 3 이상일 경우 두 항목 간의 연관성이 상당하다고 간주합니다. 향상도가 1이면 두 항목이 독립적임을 의미하고, 3이면 A가 발생할 때 B가 발생할 가능성이 3배 증가함을 의미합니다.
    • min_length = 2:
      • 생성할 규칙의 최소 항목 개수입니다. 규칙이 최소 2개의 항목을 포함해야 한다는 뜻입니다. 예를 들어, {A, B}와 같은 규칙이 생성되어야 합니다.
    • max_length = 2:
      • 생성할 규칙의 최대 항목 개수입니다. 규칙이 최대 2개의 항목을 포함해야 한다는 뜻으로, {A, B} 형태의 규칙만 생성됩니다.
rules:
  • apriori 함수 실행 후, rules 변수에는 지정된 기준을 만족하는 연관 규칙이 담긴 객체 리스트가 저장됩니다. 이 규칙들은 다음과 같은 정보를 포함할 수 있습니다:
    • 규칙(A → B)
    • 지지도(Support)
    • 신뢰도(Confidence)
    • 향상도(Lift)

 

 

Apriori 함수의 출력에서 첫 번째 결과를 표시하기


       
        results = list(rules)

        results


[RelationRecord(items=frozenset({'chicken', 'light cream'}), support=0.004532728969470737, ordered_statistics=[OrderedStatistic(items_base=frozenset({'light cream'}), items_add=frozenset({'chicken'}), confidence=0.29059829059829057, lift=4.84395061728395)]),

 RelationRecord(items=frozenset({'mushroom cream sauce', 'escalope'}), support=0.005732568990801226, ordered_statistics=[OrderedStatistic(items_base=frozenset({'mushroom cream sauce'}), items_add=frozenset({'escalope'}), confidence=0.3006993006993007, lift=3.790832696715049)]),

 RelationRecord(items=frozenset({'pasta', 'escalope'}), support=0.005865884548726837, ordered_statistics=[OrderedStatistic(items_base=frozenset({'pasta'}), items_add=frozenset({'escalope'}), confidence=0.3728813559322034, lift=4.700811850163794)]),

 RelationRecord(items=frozenset({'honey', 'fromage blanc'}), support=0.003332888948140248, ordered_statistics=[OrderedStatistic(items_base=frozenset({'fromage blanc'}), items_add=frozenset({'honey'}), confidence=0.2450980392156863, lift=5.164270764485569)]),

 RelationRecord(items=frozenset({'herb & pepper', 'ground beef'}), support=0.015997866951073192, ordered_statistics=[OrderedStatistic(items_base=frozenset({'herb & pepper'}), items_add=frozenset({'ground beef'}), confidence=0.3234501347708895, lift=3.2919938411349285)]),

 RelationRecord(items=frozenset({'tomato sauce', 'ground beef'}), support=0.005332622317024397, ordered_statistics=[OrderedStatistic(items_base=frozenset({'tomato sauce'}), items_add=frozenset({'ground beef'}), confidence=0.3773584905660377, lift=3.840659481324083)]),

 RelationRecord(items=frozenset({'olive oil', 'light cream'}), support=0.003199573390214638, ordered_statistics=[OrderedStatistic(items_base=frozenset({'light cream'}), items_add=frozenset({'olive oil'}), confidence=0.20512820512820515, lift=3.1147098515519573)]),

 RelationRecord(items=frozenset({'olive oil', 'whole wheat pasta'}), support=0.007998933475536596, ordered_statistics=[OrderedStatistic(items_base=frozenset({'whole wheat pasta'}), items_add=frozenset({'olive oil'}), confidence=0.2714932126696833, lift=4.122410097642296)]),

 RelationRecord(items=frozenset({'pasta', 'shrimp'}), support=0.005065991201173177, ordered_statistics=[OrderedStatistic(items_base=frozenset({'pasta'}), items_add=frozenset({'shrimp'}), confidence=0.3220338983050847, lift=4.506672147735896)])]

데이터 구조 설명

각 RelationRecord는 다음과 같은 정보를 포함하고 있습니다:
    1. items:
      • frozenset으로 표현된 항목 집합입니다. 이 집합은 함께 발생하는 항목들의 조합을 나타냅니다. 예를 들어, frozenset({'chicken', 'light cream'})은 'chicken'과 'light cream'이 함께 구매된 경우를 나타냅니다.
    2. support:
      • 특정 항목 조합의 지지도를 나타냅니다. 전체 트랜잭션 수에 대한 이 항목 조합의 발생 비율로, 예를 들어 support=0.004532728969470737은 이 조합이 전체 트랜잭션의 약 0.45%에서 발생했음을 의미합니다.
    3. ordered_statistics:
      • 이 항목 집합에 대한 연관 규칙을 나타내는 리스트입니다. 각 규칙은 OrderedStatistic 객체로 표현되며, 다음과 같은 속성을 포함합니다:
        • items_base: 규칙의 왼쪽 부분 (기본 항목)으로, 이 항목이 주어졌을 때 발생할 가능성이 있는 항목입니다.
        • items_add: 규칙의 오른쪽 부분 (추가 항목)으로, 기본 항목이 주어졌을 때 추가로 발생하는 항목입니다.
        • confidence: 주어진 기본 항목이 발생했을 때 추가 항목이 발생할 확률을 나타냅니다. 예를 들어, confidence=0.29059829059829057은 'light cream'이 있을 때 'chicken'이 발생할 확률이 약 29%임을 의미합니다.
        • lift: 두 항목 간의 관계 강도를 측정하는 지표로, 1보다 큰 값은 두 항목 간의 상관관계가 있다는 것을 의미합니다. 예를 들어, lift=4.84395061728395는 'light cream'과 'chicken'의 관계가 독립적일 경우보다 4.84배 더 자주 함께 발생함을 나타냅니다.

예시 해석

첫 번째 규칙인 frozenset({'chicken', 'light cream'})의 경우:
  • 이 조합은 약 0.45%의 지지도를 가지며, 'light cream'이 주어졌을 때 'chicken'이 약 29%의 확률로 추가로 구매된다는 것을 의미합니다. 또한 이 두 항목 간의 향상도는 4.84로, 이들이 함께 구매될 가능성이 높음을 나타냅니다.

 

 

결과를 잘 정리하여 Pandas DataFrame에 저장하기


       
        def inspect(results):
            lhs         = [tuple(result[2][0][0])[0] for result in results]
            rhs         = [tuple(result[2][0][1])[0] for result in results]
            supports    = [result[1] for result in results]
            confidences = [result[2][0][2] for result in results]
            lifts       = [result[2][0][3] for result in results]
            return list(zip(lhs, rhs, supports, confidences, lifts))
        resultsinDataFrame = pd.DataFrame(inspect(results), columns = ['Left Hand Side', 'Right Hand Side', 'Support', 'Confidence', 'Lift'])

 
 

 

 

결과확인하기


       
      resultsinDataFrame


 

 

 

향상도(lift)를 기준으로 내림차순으로 정렬된 결과를 표시하기


       
        resultsinDataFrame.nlargest(n = 10, columns = 'Lift')  

 

 

 

3. Eclate 실습

 

 

 결과를 잘 정리하여 Pandas DataFrame에 저장하기


       
        def inspect(results):
            lhs         = [tuple(result[2][0][0])[0] for result in results]
            rhs         = [tuple(result[2][0][1])[0] for result in results]
            supports    = [result[1] for result in results]
            return list(zip(lhs, rhs, supports))
        resultsinDataFrame = pd.DataFrame(inspect(results), columns = ['Product 1', 'Product 2', 'Support'])


 

 

 

결과확인

      

      resultsinDataFrame.nlargest(n = 10, columns = 'Support')