告别非黑即白:用Python手把手实现一个简单的模糊推理系统(附完整代码)
从数学到代码用Python构建智能穿衣推荐系统清晨起床你是否经常对着衣柜纠结今天该穿什么天气预报说今天最高温度23度但23度到底该穿毛衣还是衬衫传统编程用精确的if-else规则处理这类问题显得生硬而人类思维则更擅长处理有点凉、比较暖和这类模糊概念。本文将带你用Python实现一个基于模糊逻辑的智能穿衣推荐系统让代码也能像人类一样思考温度与穿衣之间的关系。1. 模糊逻辑基础与系统设计模糊逻辑的核心在于承认事物属性的渐变性。与传统的布尔逻辑不同模糊逻辑允许一个元素部分属于某个集合。比如18度天气可以同时属于凉爽和温暖两个集合只是隶属程度不同。我们的穿衣推荐系统将包含三个主要组件模糊化模块将精确的温度值转换为对冷、温暖、热等模糊概念的隶属度推理引擎基于专家规则如如果天气温暖则穿衣随性进行逻辑推理去模糊化模块将模糊的输出结果转换为具体的穿衣建议系统工作流程如下温度传感器 → 模糊化 → 规则推理 → 去模糊化 → 穿衣建议2. 实现模糊集合与隶属度函数我们首先定义温度和穿衣指数的模糊集合及其隶属函数。温度分为冷、温暖、热三档穿衣指数分为少、随性、多三档。import numpy as np import matplotlib.pyplot as plt class FuzzySet: def __init__(self, name, mf_type, params): self.name name self.mf_type mf_type self.params params def membership(self, x): if self.mf_type triangular: a, b, c self.params if x a or x c: return 0.0 elif a x b: return (x - a) / (b - a) else: return (c - x) / (c - b) elif self.mf_type trapezoidal: a, b, c, d self.params if x a or x d: return 0.0 elif b x c: return 1.0 elif a x b: return (x - a) / (b - a) else: return (d - x) / (d - c) return 0.0 # 定义温度模糊集 temp_sets { cold: FuzzySet(cold, trapezoidal, [-np.inf, -np.inf, 10, 15]), warm: FuzzySet(warm, triangular, [10, 20, 25]), hot: FuzzySet(hot, trapezoidal, [20, 25, np.inf, np.inf]) } # 定义穿衣指数模糊集 clothing_sets { light: FuzzySet(light, trapezoidal, [1, 1, 3, 5]), medium: FuzzySet(medium, triangular, [3, 5, 7]), heavy: FuzzySet(heavy, trapezoidal, [5, 7, 10, 10]) }可视化这些隶属函数能帮助我们更直观地理解def plot_membership(sets, title, xrange): x np.linspace(xrange[0], xrange[1], 1000) plt.figure(figsize(10, 4)) for name, fset in sets.items(): y [fset.membership(xi) for xi in x] plt.plot(x, y, labelname) plt.title(title) plt.legend() plt.grid(True) plt.show() plot_membership(temp_sets, Temperature Membership Functions, (0, 30)) plot_membership(clothing_sets, Clothing Index Membership Functions, (1, 10))3. 构建模糊规则与推理系统模糊规则通常采用如果-则(IF-THEN)的形式。我们的穿衣推荐系统包含以下三条核心规则如果温度是冷的则穿衣要多如果温度是温暖的则穿衣随性如果温度是热的则穿衣要少在模糊逻辑中这些规则将被转化为模糊关系矩阵。我们使用Mamdani推理方法这是最常用的模糊推理方法之一。class FuzzyInferenceSystem: def __init__(self, input_sets, output_sets, rules): self.input_sets input_sets self.output_sets output_sets self.rules rules def fuzzify(self, input_value): return {name: fset.membership(input_value) for name, fset in self.input_sets.items()} def infer(self, input_value): # 模糊化输入 input_memberships self.fuzzify(input_value) # 初始化输出隶属度 output_memberships {name: 0.0 for name in self.output_sets.keys()} # 应用每条规则 for rule in self.rules: input_term, output_term rule # 使用最小运算作为蕴含关系 strength input_memberships.get(input_term, 0.0) # 更新输出隶属度(使用最大运算聚合多条规则) output_memberships[output_term] max( output_memberships[output_term], strength) return output_memberships def defuzzify(self, output_memberships, methodcentroid, n_samples1000): if method centroid: # 重心法去模糊化 x np.linspace(1, 10, n_samples) y np.zeros_like(x) for name, mu in output_memberships.items(): if mu 0: y np.maximum(y, np.minimum( mu, [self.output_sets[name].membership(xi) for xi in x])) if np.sum(y) 0: return 5.0 # 默认值 return np.sum(x * y) / np.sum(y) else: raise ValueError(Unsupported defuzzification method)4. 完整系统实现与测试现在我们将所有组件整合成一个完整的穿衣推荐系统class ClothingAdvisor: def __init__(self): # 初始化模糊集 self.temp_sets { cold: FuzzySet(cold, trapezoidal, [-np.inf, -np.inf, 10, 15]), warm: FuzzySet(warm, triangular, [10, 20, 25]), hot: FuzzySet(hot, trapezoidal, [20, 25, np.inf, np.inf]) } self.clothing_sets { light: FuzzySet(light, trapezoidal, [1, 1, 3, 5]), medium: FuzzySet(medium, triangular, [3, 5, 7]), heavy: FuzzySet(heavy, trapezoidal, [5, 7, 10, 10]) } # 定义规则 self.rules [ (cold, heavy), (warm, medium), (hot, light) ] # 初始化推理系统 self.fis FuzzyInferenceSystem( self.temp_sets, self.clothing_sets, self.rules) def recommend(self, temperature): # 执行模糊推理 output_mu self.fis.infer(temperature) # 去模糊化得到具体穿衣指数 clothing_index self.fis.defuzzify(output_mu) # 将穿衣指数转换为具体建议 if clothing_index 3.5: return 建议穿轻薄衣物如短袖、短裤 elif 3.5 clothing_index 6.5: return 建议穿适中衣物如长袖T恤、薄外套 else: return 建议穿厚衣物如毛衣、厚外套测试我们的系统advisor ClothingAdvisor() test_temps [5, 15, 23, 28] for temp in test_temps: print(f温度 {temp}°C: {advisor.recommend(temp)})输出结果示例温度 5°C: 建议穿厚衣物如毛衣、厚外套 温度 15°C: 建议穿适中衣物如长袖T恤、薄外套 温度 23°C: 建议穿适中衣物如长袖T恤、薄外套 温度 28°C: 建议穿轻薄衣物如短袖、短裤5. 系统优化与扩展基础系统已经可以工作但们还可以从以下几个方面进行优化更精细的温度划分增加凉爽、炎热等中间状态考虑更多因素加入湿度、风速等天气条件改进隶属函数使用更符合人体感受的函数形状规则权重给不同规则分配不同权重学习机制根据用户反馈调整规则扩展后的规则可能如下advanced_rules [ # 温度和穿衣规则 (very_cold, very_heavy, 1.0), (cold, heavy, 0.9), (cool, medium_heavy, 0.8), (warm, medium, 0.7), (hot, light, 0.9), (very_hot, very_light, 1.0), # 考虑湿度的规则 ((warm, humid), light, 0.6), ((cool, humid), medium_heavy, 0.7) ]实现加权规则推理需要对FuzzyInferenceSystem类进行扩展def infer(self, input_values): # 支持多输入 if not isinstance(input_values, dict): input_values {temperature: input_values} # 模糊化所有输入 fuzzified {} for var_name, value in input_values.items(): fuzzified[var_name] { fset.name: fset.membership(value) for fset in self.input_sets[var_name].values() } # 初始化输出隶属度 output_memberships {name: 0.0 for name in self.output_sets.keys()} # 应用每条规则 for rule in self.rules: antecedents, consequent, weight rule # 计算规则前件强度 if isinstance(antecedents, str): # 单条件 strength fuzzified[temperature].get(antecedents, 0.0) else: # 多条件组合 strength 1.0 for ant in antecedents: for var_name, terms in fuzzified.items(): if ant in terms: strength min(strength, terms[ant]) break # 应用权重 strength * weight # 更新输出隶属度 output_memberships[consequent] max( output_memberships[consequent], strength) return output_memberships6. 实际应用与部署建议将模糊系统部署到实际应用中时有几个实用建议性能优化对于实时系统可以预计算隶属度值并存储为查找表用户界面可视化模糊推理过程有助于增强用户信任A/B测试比较模糊系统与传统规则系统的效果日志记录记录系统的输入输出用于后续分析和改进一个简单的Flask Web应用接口示例from flask import Flask, request, jsonify app Flask(__name__) advisor ClothingAdvisor() app.route(/recommend, methods[GET]) def get_recommendation(): temp float(request.args.get(temp, 20.0)) recommendation advisor.recommend(temp) return jsonify({ temperature: temp, recommendation: recommendation }) if __name__ __main__: app.run(debugTrue)调用示例curl http://localhost:5000/recommend?temp18响应示例{ temperature: 18, recommendation: 建议穿适中衣物如长袖T恤、薄外套 }在开发过程中我发现三角隶属函数在大多数情况下已经足够好用但对于极端温度范围极冷或极热梯形函数能提供更好的控制。另外去模糊化方法的选择对系统行为有显著影响重心法通常能产生最自然的结果但在实时性要求高的场景中最大值法可能更合适。