ShopTRAINING/docs/输出文档/预测结果分析增强.md

23 KiB
Raw Permalink Blame History

🔮 药店销售预测结果分析增强方案

一、预测结果解释系统设计 📊

1. 预测解释功能概述

在现有的预测系统基础上,增加预测结果解释功能,不仅返回预测值,还提供对预测结果的分析解释,帮助用户理解"为什么会得到这样的预测结果"和"哪些因素影响了这个预测"。

2. 核心功能模块

A. 特征重要性分析

def analyze_feature_importance(model, X_test, feature_names):
    """
    分析各个特征对预测结果的重要性
    
    Args:
        model: 训练好的模型
        X_test: 测试数据特征
        feature_names: 特征名称列表
        
    Returns:
        feature_importance_dict: 特征重要性字典
    """
    # 根据不同模型类型实现特征重要性计算
    if hasattr(model, 'feature_importances_'):  # 如GBDT等树模型
        importances = model.feature_importances_
    else:  # 对于深度学习模型使用排列重要性或SHAP值
        importances = calculate_permutation_importance(model, X_test)
    
    # 将特征名称与重要性值匹配
    feature_importance_dict = dict(zip(feature_names, importances))
    return feature_importance_dict

B. 预测因素分解

def decompose_prediction_factors(prediction, feature_values, feature_importance):
    """
    将预测结果分解为各个因素的贡献
    
    Args:
        prediction: 预测结果
        feature_values: 输入特征值
        feature_importance: 特征重要性
        
    Returns:
        factor_contributions: 各因素对预测的贡献
    """
    # 计算每个特征的贡献
    factor_contributions = {}
    for feature, value in feature_values.items():
        if feature in feature_importance:
            contribution = value * feature_importance[feature]
            factor_contributions[feature] = {
                'value': value,
                'importance': feature_importance[feature],
                'contribution': contribution
            }
    
    # 按贡献大小排序
    sorted_factors = sorted(factor_contributions.items(), 
                           key=lambda x: abs(x[1]['contribution']), 
                           reverse=True)
    
    return sorted_factors

C. 历史模式匹配

def find_similar_historical_patterns(current_prediction, historical_data, window_size=7):
    """
    查找历史数据中与当前预测模式相似的时间段
    
    Args:
        current_prediction: 当前预测结果序列
        historical_data: 历史数据
        window_size: 比较窗口大小
        
    Returns:
        similar_patterns: 相似的历史模式列表
    """
    similar_patterns = []
    
    # 计算当前预测的特征(如趋势、周期性等)
    current_features = extract_pattern_features(current_prediction)
    
    # 在历史数据中滑动窗口寻找相似模式
    for i in range(len(historical_data) - window_size):
        window = historical_data[i:i+window_size]
        window_features = extract_pattern_features(window)
        
        # 计算相似度
        similarity = calculate_similarity(current_features, window_features)
        
        if similarity > SIMILARITY_THRESHOLD:
            similar_patterns.append({
                'period': (i, i+window_size),
                'data': window,
                'similarity': similarity
            })
    
    # 按相似度排序
    similar_patterns.sort(key=lambda x: x['similarity'], reverse=True)
    
    return similar_patterns[:5]  # 返回前5个最相似的模式

D. 异常值检测与解释

def detect_and_explain_anomalies(predictions, historical_stats):
    """
    检测预测结果中的异常值并提供解释
    
    Args:
        predictions: 预测结果序列
        historical_stats: 历史数据统计信息
        
    Returns:
        anomalies: 异常值及其解释
    """
    anomalies = []
    
    # 计算预测值的统计特性
    mean = historical_stats['mean']
    std = historical_stats['std']
    
    # 检测异常值(例如超过2个标准差)
    for i, value in enumerate(predictions):
        z_score = (value - mean) / std
        
        if abs(z_score) > 2:
            # 异常值检测
            anomaly = {
                'day': i,
                'value': value,
                'z_score': z_score,
                'severity': 'high' if abs(z_score) > 3 else 'medium',
                'explanation': generate_anomaly_explanation(value, z_score, i)
            }
            anomalies.append(anomaly)
    
    return anomalies

二、预测结果解释API设计 🔌

1. API接口扩展

@app.route('/api/prediction/explain', methods=['POST'])
def explain_prediction():
    """
    提供预测结果的详细解释
    ---
    tags:
      - 预测分析
    parameters:
      - name: body
        in: body
        required: true
        schema:
          type: object
          properties:
            product_id:
              type: string
              example: P001
            model_type:
              type: string
              enum: [mlstm, transformer, kan, optimized_kan]
            future_days:
              type: integer
              default: 7
            include_factors:
              type: boolean
              default: true
            include_similar_patterns:
              type: boolean
              default: true
            include_anomaly_detection:
              type: boolean
              default: true
    responses:
      200:
        description: 预测结果及其解释
    """
    data = request.json
    product_id = data.get('product_id')
    model_type = data.get('model_type')
    future_days = data.get('future_days', 7)
    
    # 获取预测结果
    predictions, features = load_model_and_predict_with_features(
        product_id, model_type, future_days
    )
    
    # 准备响应数据
    response = {
        'status': 'success',
        'predictions': predictions.tolist(),
        'explanation': {}
    }
    
    # 根据请求参数添加不同类型的解释
    if data.get('include_factors', True):
        # 添加特征重要性分析
        model = load_model(product_id, model_type)
        feature_importance = analyze_feature_importance(model, features, FEATURE_NAMES)
        factor_contributions = decompose_prediction_factors(
            predictions, features, feature_importance
        )
        response['explanation']['factors'] = factor_contributions
    
    if data.get('include_similar_patterns', True):
        # 添加历史模式匹配
        historical_data = load_historical_data(product_id)
        similar_patterns = find_similar_historical_patterns(
            predictions, historical_data
        )
        response['explanation']['similar_patterns'] = similar_patterns
    
    if data.get('include_anomaly_detection', True):
        # 添加异常值检测
        historical_stats = calculate_historical_stats(product_id)
        anomalies = detect_and_explain_anomalies(predictions, historical_stats)
        response['explanation']['anomalies'] = anomalies
    
    # 生成总体解释文本
    response['explanation']['summary'] = generate_explanation_summary(
        predictions, response['explanation']
    )
    
    return jsonify(response)

2. 前端展示组件

A. 预测结果解释面板

// Vue组件示例
<template>
  <div class="prediction-explanation">
    <el-card class="explanation-card">
      <div slot="header">
        <span>预测结果解释</span>
      </div>
      
      <!-- 总体解释摘要 -->
      <div class="explanation-summary">
        <h4>预测分析摘要</h4>
        <p>{{ explanation.summary }}</p>
      </div>
      
      <!-- 关键影响因素 -->
      <div v-if="explanation.factors" class="key-factors">
        <h4>关键影响因素</h4>
        <el-table :data="explanation.factors.slice(0, 5)" style="width: 100%">
          <el-table-column prop="factor" label="因素"></el-table-column>
          <el-table-column prop="contribution" label="贡献度">
            <template slot-scope="scope">
              <el-progress 
                :percentage="Math.abs(scope.row.contribution * 100)" 
                :color="scope.row.contribution > 0 ? '#67C23A' : '#F56C6C'"
              ></el-progress>
            </template>
          </el-table-column>
          <el-table-column prop="impact" label="影响">
            <template slot-scope="scope">
              <span :class="scope.row.contribution > 0 ? 'positive' : 'negative'">
                {{ scope.row.contribution > 0 ? '增加' : '减少' }}
              </span>
            </template>
          </el-table-column>
        </el-table>
      </div>
      
      <!-- 异常值提示 -->
      <div v-if="explanation.anomalies && explanation.anomalies.length > 0" class="anomalies">
        <h4>异常值提示</h4>
        <el-alert
          v-for="(anomaly, index) in explanation.anomalies"
          :key="index"
          :title="`第${anomaly.day + 1}天: ${anomaly.value} (${anomaly.severity}级异常)`"
          :type="anomaly.severity === 'high' ? 'error' : 'warning'"
          :description="anomaly.explanation"
          show-icon
        ></el-alert>
      </div>
      
      <!-- 相似历史模式 -->
      <div v-if="explanation.similar_patterns && explanation.similar_patterns.length > 0" class="similar-patterns">
        <h4>相似历史模式</h4>
        <el-collapse>
          <el-collapse-item 
            v-for="(pattern, index) in explanation.similar_patterns" 
            :key="index"
            :title="`相似模式 #${index + 1} (相似度: ${(pattern.similarity * 100).toFixed(1)}%)`"
          >
            <div class="pattern-chart">
              <line-chart :chart-data="prepareChartData(pattern.data)"></line-chart>
            </div>
            <div class="pattern-period">
              <p>历史时段: {{ formatDateRange(pattern.period) }}</p>
            </div>
          </el-collapse-item>
        </el-collapse>
      </div>
    </el-card>
  </div>
</template>

三、预测解释生成器实现 🧩

1. 解释文本生成器

def generate_explanation_summary(predictions, explanation_data):
    """
    根据预测结果和解释数据生成人类可读的解释摘要
    
    Args:
        predictions: 预测结果序列
        explanation_data: 包含各种解释数据的字典
        
    Returns:
        summary: 人类可读的解释摘要
    """
    summary = []
    
    # 分析预测趋势
    trend = analyze_trend(predictions)
    if trend == 'increasing':
        summary.append("预测显示未来销量呈上升趋势。")
    elif trend == 'decreasing':
        summary.append("预测显示未来销量呈下降趋势。")
    else:
        summary.append("预测显示未来销量相对稳定。")
    
    # 添加关键影响因素解释
    if 'factors' in explanation_data:
        top_factors = explanation_data['factors'][:3]  # 取前三个最重要的因素
        factor_text = "主要影响因素包括: "
        for i, (factor, data) in enumerate(top_factors):
            direction = "增加" if data['contribution'] > 0 else "减少"
            if i > 0:
                factor_text += "、"
            factor_text += f"{factor}({direction})"
        summary.append(factor_text)
    
    # 添加异常值解释
    if 'anomalies' in explanation_data and explanation_data['anomalies']:
        anomaly_count = len(explanation_data['anomalies'])
        summary.append(f"预测中发现{anomaly_count}个异常值,可能需要特别关注。")
    
    # 添加历史模式参考
    if 'similar_patterns' in explanation_data and explanation_data['similar_patterns']:
        top_pattern = explanation_data['similar_patterns'][0]
        similarity = top_pattern['similarity'] * 100
        summary.append(f"当前预测模式与历史数据中的某些时段有{similarity:.1f}%的相似度,可参考历史表现。")
    
    # 生成建议
    recommendations = generate_recommendations(predictions, explanation_data)
    if recommendations:
        summary.append("建议: " + recommendations)
    
    return " ".join(summary)

2. 特定场景解释生成

def generate_anomaly_explanation(value, z_score, day_index):
    """
    为异常值生成解释
    
    Args:
        value: 异常预测值
        z_score: Z分数(标准差倍数)
        day_index: 异常值所在的天数索引
        
    Returns:
        explanation: 异常值解释文本
    """
    weekday = (datetime.now() + timedelta(days=day_index)).strftime("%A")
    
    if z_score > 0:
        if weekday in ['Saturday', 'Sunday']:
            return f"该值异常偏高,可能与周末效应有关。周末通常客流量增加,销量上升。"
        else:
            return f"该值异常偏高,可能受到促销活动、季节性因素或特殊事件影响。"
    else:
        if weekday in ['Saturday', 'Sunday']:
            return f"该值在周末异常偏低,与典型周末销售模式不符,可能有特殊原因导致客流减少。"
        else:
            return f"该值异常偏低,可能受到库存问题、竞争对手活动或外部因素影响。"

3. 建议生成器

def generate_recommendations(predictions, explanation_data):
    """
    基于预测结果和解释数据生成行动建议
    
    Args:
        predictions: 预测结果序列
        explanation_data: 解释数据字典
        
    Returns:
        recommendations: 建议文本
    """
    recommendations = []
    
    # 分析预测趋势
    trend = analyze_trend(predictions)
    
    # 基于趋势的库存建议
    if trend == 'increasing':
        recommendations.append("考虑增加采购量,确保库存充足")
    elif trend == 'decreasing':
        recommendations.append("考虑减少采购,避免库存积压")
    
    # 基于异常值的建议
    if 'anomalies' in explanation_data and explanation_data['anomalies']:
        high_anomalies = [a for a in explanation_data['anomalies'] if a['severity'] == 'high']
        if high_anomalies:
            recommendations.append("关注高度异常的预测日期,制定应对预案")
    
    # 基于特征重要性的建议
    if 'factors' in explanation_data:
        weather_factors = [f for f, _ in explanation_data['factors'] if 'temperature' in f or 'weather' in f]
        if weather_factors:
            recommendations.append("密切关注天气变化对销量的影响")
        
        promotion_factors = [f for f, _ in explanation_data['factors'] if 'promotion' in f]
        if promotion_factors:
            recommendations.append("促销活动对销量影响显著,可考虑优化促销策略")
    
    return "".join(recommendations) + "。" if recommendations else ""

四、实施与集成方案 🔧

1. 系统集成步骤

  1. 后端API实现:

    • pharmacy_predictor.py中添加预测解释相关函数
    • api.py中添加新的API端点
  2. 数据存储扩展:

    • 添加特征重要性存储
    • 保存历史预测与实际销量对比数据
  3. 前端集成:

    • UI/src/views/PredictionView.vue中添加解释面板组件
    • 添加可视化图表展示影响因素

2. 代码实现示例

A. 后端实现

# 在pharmacy_predictor.py中添加

def explain_model_prediction(product_id, model_type, predictions, features):
    """
    为模型预测结果提供解释
    """
    # 加载模型
    model_path = get_model_path(product_id, model_type)
    model = torch.load(model_path)
    
    # 获取特征重要性
    feature_names = ['sales', 'price', 'weekday', 'month', 'is_holiday', 
                     'is_weekend', 'is_promotion', 'temperature']
    feature_importance = analyze_feature_importance(model, features, feature_names)
    
    # 获取历史数据
    historical_data = load_historical_sales_data(product_id)
    
    # 生成解释
    explanation = {
        'feature_importance': feature_importance,
        'similar_patterns': find_similar_historical_patterns(predictions, historical_data),
        'anomalies': detect_and_explain_anomalies(predictions, calculate_historical_stats(historical_data))
    }
    
    # 生成总结
    explanation['summary'] = generate_explanation_summary(predictions, explanation)
    
    return explanation

B. API端点实现

# 在api.py中添加

@app.route('/api/prediction/explain', methods=['POST'])
def explain_prediction():
    """
    提供预测结果的详细解释
    """
    try:
        data = request.json
        product_id = data.get('product_id')
        model_type = data.get('model_type')
        future_days = data.get('future_days', 7)
        
        # 验证参数
        if not product_id or not model_type:
            return jsonify({"status": "error", "error": "缺少必要参数"}), 400
        
        # 获取预测结果和特征
        predictions, features = load_model_and_predict_with_features(
            product_id, model_type, future_days
        )
        
        # 生成解释
        explanation = explain_model_prediction(
            product_id, model_type, predictions, features
        )
        
        return jsonify({
            "status": "success",
            "product_id": product_id,
            "model_type": model_type,
            "predictions": predictions.tolist(),
            "explanation": explanation
        })
        
    except Exception as e:
        return jsonify({"status": "error", "error": str(e)}), 500

3. UI实现示例

<!-- 在PredictionView.vue中添加 -->
<template>
  <div>
    <!-- 现有预测结果展示 -->
    <div class="prediction-results">
      <!-- ... 现有代码 ... -->
    </div>
    
    <!-- 新增预测解释面板 -->
    <el-collapse v-if="predictionExplanation">
      <el-collapse-item title="预测结果解释" name="explanation">
        <div class="explanation-summary">
          <el-alert
            :title="predictionExplanation.summary"
            type="info"
            :closable="false"
            show-icon
          ></el-alert>
        </div>
        
        <!-- 关键影响因素 -->
        <div class="key-factors-section">
          <h4>关键影响因素</h4>
          <el-table
            :data="keyFactors"
            style="width: 100%"
          >
            <el-table-column prop="factor" label="因素名称"></el-table-column>
            <el-table-column prop="importance" label="重要性">
              <template slot-scope="scope">
                <el-progress :percentage="scope.row.importance * 100"></el-progress>
              </template>
            </el-table-column>
            <el-table-column prop="impact" label="影响方向">
              <template slot-scope="scope">
                <span :class="scope.row.impact > 0 ? 'positive-impact' : 'negative-impact'">
                  {{ scope.row.impact > 0 ? '正面影响' : '负面影响' }}
                </span>
              </template>
            </el-table-column>
          </el-table>
        </div>
        
        <!-- 异常值检测 -->
        <div v-if="predictionExplanation.anomalies && predictionExplanation.anomalies.length > 0" class="anomalies-section">
          <h4>异常值检测</h4>
          <el-card v-for="(anomaly, index) in predictionExplanation.anomalies" :key="index" class="anomaly-card">
            <div slot="header">
              <span>{{ anomaly.day + 1 }}天异常: {{ anomaly.value }}</span>
            </div>
            <p>{{ anomaly.explanation }}</p>
          </el-card>
        </div>
        
        <!-- 相似历史模式 -->
        <div v-if="predictionExplanation.similar_patterns && predictionExplanation.similar_patterns.length > 0" class="similar-patterns-section">
          <h4>相似历史模式</h4>
          <el-tabs>
            <el-tab-pane 
              v-for="(pattern, index) in predictionExplanation.similar_patterns.slice(0, 3)" 
              :key="index"
              :label="`模式 ${index + 1} (相似度: ${(pattern.similarity * 100).toFixed(1)}%)`"
            >
              <div class="pattern-chart">
                <line-chart :chart-data="getPatternChartData(pattern)"></line-chart>
              </div>
            </el-tab-pane>
          </el-tabs>
        </div>
      </el-collapse-item>
    </el-collapse>
  </div>
</template>

<script>
export default {
  // ... 现有代码 ...
  
  data() {
    return {
      // ... 现有数据 ...
      predictionExplanation: null,
      keyFactors: []
    };
  },
  
  methods: {
    // ... 现有方法 ...
    
    async getPrediction() {
      // 获取预测结果
      const response = await this.fetchPrediction();
      
      // 获取预测解释
      this.fetchPredictionExplanation();
    },
    
    async fetchPredictionExplanation() {
      try {
        const response = await fetch('/api/prediction/explain', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            product_id: this.form.product_id,
            model_type: this.form.model_type,
            future_days: this.form.future_days
          })
        });
        
        const data = await response.json();
        
        if (data.status === 'success') {
          this.predictionExplanation = data.explanation;
          this.keyFactors = this.formatKeyFactors(data.explanation.feature_importance);
        }
      } catch (error) {
        console.error('获取预测解释失败:', error);
      }
    },
    
    formatKeyFactors(featureImportance) {
      // 将特征重要性数据转换为表格数据
      return Object.entries(featureImportance)
        .map(([factor, value]) => ({
          factor,
          importance: Math.abs(value),
          impact: value
        }))
        .sort((a, b) => b.importance - a.importance)
        .slice(0, 5); // 只显示前5个最重要的因素
    },
    
    getPatternChartData(pattern) {
      // 准备图表数据
      return {
        labels: Array.from({length: pattern.data.length}, (_, i) => `Day ${i+1}`),
        datasets: [{
          label: '历史销量',
          data: pattern.data,
          borderColor: '#409EFF',
          fill: false
        }]
      };
    }
  }
};
</script>

五、应用场景与优势 🌟

1. 应用场景

  • 库存管理决策支持:解释预测结果背后的原因,帮助制定更精准的库存计划
  • 销售策略优化:理解影响销量的关键因素,优化促销和定价策略
  • 异常情况预警:提前识别可能的销售异常,制定应对措施
  • 多模型比较分析:解释不同模型预测结果的差异原因,选择最适合的模型

2. 系统优势

  • 提高可解释性:将"黑盒"模型转变为可理解的决策支持工具
  • 增强用户信任:通过解释预测背后的逻辑,增强用户对预测结果的信任
  • 辅助决策制定:提供数据支持的建议,帮助用户做出更明智的决策
  • 持续学习优化:通过解释系统,识别模型的不足和改进方向

3. 未来扩展方向

  • 交互式解释:允许用户通过调整输入参数,实时观察对预测结果的影响
  • 场景模拟:基于不同假设条件(如天气变化、促销力度)模拟预测结果
  • 自动化建议系统:根据预测解释自动生成库存管理和销售策略建议
  • 多维度可视化:提供更丰富的可视化工具,展示预测结果与各因素的关系 </rewritten_file>