Fuzzy라는 단어는, 흐릿한, 불분명한이라는 의미를 가진다. 여기서도 알 수 있듯, 퍼지 논리(Fuzzy Logic)는 명확하게 구분되지 않는 개념을 수학적으로 다루는 방법, 다시 말해 정확하지 않거나(imprecise) 불완전한(incomplete) 정보를 처리하는 데 유용한 논리 체계를 의미한다. 이는 1965년 L. A. Zadeh가 퍼지 집합 이론을 제시하면서 등장했고, 이후 퍼지 명제나 규칙을 다루기 위한 퍼지 논리로 발전하였다.
원소가 속하거나 속하지 않는 두 가지로만 구분되는 크리스프 집합과는 달리, 퍼지 집합에서는 원소가 특정 집합에 속하는 정도를 표현함.
그렇다고 해서, 퍼지 논리가 애매한 논리인 것은 아니다. 퍼지 논리는 애매함을 다루는 질서정연한 논리이다.


맘다니형 추론 : 가장 흔히 쓰이는 퍼지 추론 기법
1단계 : 입력 변수의 퍼지화
2단계 : 규칙 평가
3단계 : 출력으로 나온 규칙의 통합
4단계 : 역퍼지화
맘다니형 추론 예시 - 입력 2개, 출력 1개, 규칙 3개 문제 예시

1단계 : 입력 변수의 퍼지화
2단계 : 규칙 평가

3단계 : 출력으로 나온 규칙의 통합
4단계 : 역퍼지화

스게노형 추론 : 퍼지 집합 대신 입력 변수에 대한 수학 함수를 사용하는 추론 방법
스게노형 퍼지 규칙 형식
IF x가 A
AND y가 B
THEN Z는 f(x, y)
// x, y, z : 언어 변수
// A, B : 논의 영역을 x, y로 하는 퍼지 집합
// f(x, y) : 수학 함수


맘다니형 추론과 스게노형 추론의 차이
!pip install scikit-fuzzy matplotlib --quiet
import numpy as np
import skfuzzy as fuzz
import matplotlib.pyplot as plt
# -----------------------------------------------------
# 1. 변수 정의
# -----------------------------------------------------
x_temp = np.arange(0, 41, 1) # 0부터 40까지 숫자 배열 (온도)
x_fan = np.arange(0, 101, 1) # 0부터 100까지 숫자 배열 (팬 속도)
# -----------------------------------------------------
# 2. 소속 함수 정의 (Membership function를 정의)
# -----------------------------------------------------
# fuzz.trimf : Triangular membership function
# syntax : y = trimf(x,params)
# (입력값, [삼각형 소속 함수 세 꼭짓점 좌표])
# (왼쪽 시작점, 꼭짓점(최대 소속도=1), 오른쪽 끝점)
temp_low = fuzz.trimf(x_temp, [0, 0, 20])
temp_med = fuzz.trimf(x_temp, [10, 20, 30])
temp_high = fuzz.trimf(x_temp, [20, 40, 40])
fan_low = fuzz.trimf(x_fan, [0, 0, 50])
fan_med = fuzz.trimf(x_fan, [20, 50, 75])
fan_high = fuzz.trimf(x_fan, [50, 100, 100])
# -----------------------------------------------------
# 3. 입력값 설정
# -----------------------------------------------------
# fuzz.interp_membership(x, xmf, xx) : 특정 입력값에 대한 소속도(멤버십 값) 계산
# x : 입력 범위, xmf : 퍼지 집합의 멤버십 함수, xx : 평가 원하는 값
temp_value = 28
temp_level_low = fuzz.interp_membership(x_temp, temp_low, temp_value) # 범위밖 -> 소속도 : 0
temp_level_med = fuzz.interp_membership(x_temp, temp_med, temp_value) # (c - x) / (c - b) -> (30 - 28) / (30 - 20) -> 소속도 : 0.2
temp_level_high = fuzz.interp_membership(x_temp, temp_high, temp_value) # (x - a) / (b - a) -> (28 - 20) / (40 - 20) -> 소속도 : 0.4
print("소속도(28도) : 낮음 = %.2f, 보통 = %.2f, 높음 = %.2f" % (temp_level_low, temp_level_med, temp_level_high))
# -----------------------------------------------------
# 4. 규칙 적용
# -----------------------------------------------------
fan_activation_low = np.fmin(temp_level_low, fan_low) # IF 온도가 낮음 → 팬 속도 낮음
fan_activation_med = np.fmin(temp_level_med, fan_med) # IF 온도가 보통 → 팬 속도 보통
fan_activation_high = np.fmin(temp_level_high, fan_high) # IF 온도가 높음 → 팬 속도 높음
# -----------------------------------------------------
# 5. 결과 결합 및 역퍼지화
# -----------------------------------------------------
# np.fmax(a, b) : 두 값 중 큰 값 반환 (OR 연산)
# aggregated : 소속도가 가장 큰 값 찾는 거
aggregated = np.fmax(fan_activation_low, np.fmax(fan_activation_med, fan_activation_high))
# fuzz.defuzz(x, mf, 'centroid') : 퍼지값을 실제 값으로 반환
# 'centroid' : 집합의 평균 계산
# 'bisector' : 곡선을 반으로 나눈 값
# 'mom/som/lom' : 최대 소속도를 갖는 값들 중 각각 중앙/최소/최대값
print(aggregated)
fan_result = fuzz.defuzz(x_fan, aggregated, 'centroid')
print("결정된 팬 속도 : %.2f %%" % fan_result)
numerator = np.sum(x_fan * aggregated)
denominator = np.sum(aggregated)
fan_result = numerator / denominator
print(numerator, denominator, fan_result)
# -----------------------------------------------------
# 6. 시각화
# -----------------------------------------------------
plt.figure(figsize = (8, 4))
plt.plot(x_fan, fan_low, 'b', linewidth=1.5, label='Low')
plt.plot(x_fan, fan_med, 'g', linewidth=1.5, label='Medium')
plt.plot(x_fan, fan_high, 'r', linewidth=1.5, label='High')
plt.axvline(fan_result, color='k', linestyle='--', label="Output (defuzzified)")
plt.legend()
plt.title("Fuzzy Inference Result (Fan Speed)")
plt.show()
# -----------------------------------------------------
# 1. 변수 정의
# -----------------------------------------------------
x_service = np.arange(0, 11, 1) # 0부터 10까지 숫자 배열 (서비스)
x_food = np.arange(0, 11, 1) # 0부터 10까지 숫자 배열 (음식 맛)
x_tip = np.arange(0, 31, 1) # 0부터 30까지 숫자 배열 (팁 퍼센트)
# -----------------------------------------------------
# 2. 소속 함수 정의
# -----------------------------------------------------
# fuzz.trimf : Triangular membership function
# (입력값, [삼각형 소속 함수 세 꼭짓점 좌표])
# 서비스 : 0 ~ 10
service_low = fuzz.trimf(x_service, [0, 0, 5])
service_med = fuzz.trimf(x_service, [0, 5, 10])
service_high = fuzz.trimf(x_service, [5, 10, 10])
# 음식 맛 : 0 ~ 10
food_bad = fuzz.trimf(x_food, [0, 0, 5])
food_ok = fuzz.trimf(x_food, [0, 5, 10])
food_good = fuzz.trimf(x_food, [5, 10, 10])
# 팁 : 0 ~ 30
tip_low = fuzz.trimf(x_tip, [0, 0, 15])
tip_med = fuzz.trimf(x_tip, [0, 15, 20])
tip_high = fuzz.trimf(x_tip, [15, 30, 30])
# -----------------------------------------------------
# 3. 입력값 (서비스 = 7, 음식 = 8)
# -----------------------------------------------------
# fuzz.interp_membership(x, xmf, xx) : 특정 입력값에 대한 소속도(멤버십 값) 계산
# x : 입력 범위, xmf : 퍼지 집합의 멤버십 함수, xx : 평가 원하는 값
service_val = 7
food_val = 8
service_level_low = fuzz.interp_membership(x_service, service_low, service_val) # 범위 밖
service_level_med = fuzz.interp_membership(x_service, service_med, service_val) # 높음
service_level_high = fuzz.interp_membership(x_service, service_high, service_val) # 높음
print("소속도(서비스 = 7) : 낮음 = %.2f, 높음 = %.2f, 높음 = %.2f" % (service_level_low, service_level_med, service_level_high))
food_level_bad = fuzz.interp_membership(x_food, food_bad, food_val) # 범위 밖
food_level_ok = fuzz.interp_membership(x_food, food_ok, food_val) # 높음
food_level_good = fuzz.interp_membership(x_food, food_good, food_val) # 높음
print("소속도(음식 = 8) : 낮음 = %.2f, 높음 = %.2f, 높음 = %.2f" % (food_level_bad, food_level_ok, food_level_good))
# -----------------------------------------------------
# 4. 규칙 적용
# -----------------------------------------------------
rule1 = np.fmax(service_level_low, food_level_bad) # 서비스가 나쁘거나 음식이 별로면
activation_rule1 = np.fmin(rule1, tip_low) # 팁 조금
rule2 = service_level_med # 서비스 중간
activation_rule2 = np.fmin(rule2, tip_med) # 팁 중간
rule3 = np.fmax(service_level_high, food_level_good) # 서비스도 좋고 음식도 좋음
activation_rule3 = np.fmin(rule3, tip_high) # 팁 많이
# -----------------------------------------------------
# 5. 결과 결합 및 역퍼지화
# -----------------------------------------------------
aggregate = np.fmax(activation_rule1, np.fmax(activation_rule1, activation_rule2))
print(aggregate)
tip_result = fuzz.defuzz(x_tip, aggregate, 'centroid')
print("결정된 팁 : %.2f %%" % tip_result)
numerator = np.sum(x_tip * aggregate)
denominator = np.sum(aggregate)
fan_result = numerator / denominator
print(numerator, denominator, fan_result)
# -----------------------------------------------------
# 6. 시각화
# -----------------------------------------------------
plt.figure(figsize = (8, 4))
plt.plot(x_tip, tip_low, 'b', linewidth=1.5, label='Low')
plt.plot(x_tip, tip_med, 'g', linewidth=1.5, label='Medium')
plt.plot(x_tip, tip_high, 'r', linewidth=1.5, label='High')
plt.axvline(tip_result, color='k', linestyle='--', label="Output (defuzzified)")
plt.legend()
plt.title("Fuzzy Inference Result (Tip Percentage)")
plt.show()