import streamlit as st import numpy as np import polars as pl from pathlib import Path import sys sys.path.insert(0, str(Path(__file__).parent)) from agent_app import create_agent from features import TransactionFeatures, DecisionResult, TransactionClass, ConfidenceLevel, Priority st.set_page_config( page_title="信用卡欺诈检测系统", page_icon="💳", layout="wide" ) st.title("💳 信用卡欺诈检测系统") st.markdown("基于机器学习的实时欺诈检测与决策支持系统") @st.cache_resource def load_agent(): return create_agent() agent = load_agent() @st.cache_data def load_csv_file(uploaded_file): if uploaded_file is not None: try: df = pl.read_csv(uploaded_file, schema_overrides={"Time": pl.Float64}) return df except Exception as e: st.error(f"读取CSV文件失败: {e}") return None return None st.sidebar.header("系统信息") st.sidebar.info(f""" **模型信息** - 模型类型: RandomForest - 特征数量: 30 - 支持工具: 2个 - predict_fraud (ML工具) - analyze_transaction """) st.header("输入交易数据") input_mode = st.radio( "选择输入方式", ["📁 上传CSV文件", "✏️ 手动输入特征"], horizontal=True ) if input_mode == "📁 上传CSV文件": st.subheader("上传CSV文件") uploaded_file = st.file_uploader( "选择CSV文件", type=['csv'], help="上传包含交易数据的CSV文件" ) if uploaded_file is not None: df = load_csv_file(uploaded_file) if df is not None: st.success(f"✅ 成功加载CSV文件,共 {df.height} 条交易记录") st.write("### 数据预览") st.dataframe(df.head(10), use_container_width=True) st.write("### 选择交易") if "Class" in df.columns: df = df.drop("Class") row_index = st.number_input( "选择交易行号(从0开始)", min_value=0, max_value=df.height - 1, value=0, step=1 ) if st.button("📋 加载选中的交易", type="primary"): feature_names = [ 'Time', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'V7', 'V8', 'V9', 'V10', 'V11', 'V12', 'V13', 'V14', 'V15', 'V16', 'V17', 'V18', 'V19', 'V20', 'V21', 'V22', 'V23', 'V24', 'V25', 'V26', 'V27', 'V28', 'Amount' ] transaction = [df[row_index, col] for col in feature_names] st.session_state.transaction = transaction st.success(f"✅ 已加载第 {row_index} 行的交易数据") st.write("### 选中的交易数据") feature_data = { "特征名称": feature_names, "特征值": [f"{v:.6f}" for v in transaction] } st.dataframe( pl.DataFrame(feature_data), use_container_width=True ) else: st.subheader("手动输入特征") col1, col2 = st.columns(2) with col1: st.write("基础信息") time = st.number_input("Time (交易时间)", value=0.0, step=1.0) amount = st.number_input("Amount (交易金额)", value=100.0, step=1.0) with col2: st.write("PCA特征 V1-V14") v1 = st.number_input("V1", value=0.0, step=0.1) v2 = st.number_input("V2", value=0.0, step=0.1) v3 = st.number_input("V3", value=0.0, step=0.1) v4 = st.number_input("V4", value=0.0, step=0.1) v5 = st.number_input("V5", value=0.0, step=0.1) v6 = st.number_input("V6", value=0.0, step=0.1) v7 = st.number_input("V7", value=0.0, step=0.1) v8 = st.number_input("V8", value=0.0, step=0.1) v9 = st.number_input("V9", value=0.0, step=0.1) v10 = st.number_input("V10", value=0.0, step=0.1) v11 = st.number_input("V11", value=0.0, step=0.1) v12 = st.number_input("V12", value=0.0, step=0.1) v13 = st.number_input("V13", value=0.0, step=0.1) v14 = st.number_input("V14", value=0.0, step=0.1) col3, col4 = st.columns(2) with col3: st.write("PCA特征 V15-V21") v15 = st.number_input("V15", value=0.0, step=0.1) v16 = st.number_input("V16", value=0.0, step=0.1) v17 = st.number_input("V17", value=0.0, step=0.1) v18 = st.number_input("V18", value=0.0, step=0.1) v19 = st.number_input("V19", value=0.0, step=0.1) v20 = st.number_input("V20", value=0.0, step=0.1) v21 = st.number_input("V21", value=0.0, step=0.1) with col4: st.write("PCA特征 V22-V28") v22 = st.number_input("V22", value=0.0, step=0.1) v23 = st.number_input("V23", value=0.0, step=0.1) v24 = st.number_input("V24", value=0.0, step=0.1) v25 = st.number_input("V25", value=0.0, step=0.1) v26 = st.number_input("V26", value=0.0, step=0.1) v27 = st.number_input("V27", value=0.0, step=0.1) v28 = st.number_input("V28", value=0.0, step=0.1) st.divider() if st.button("🔍 检测欺诈", type="primary", use_container_width=True): if input_mode == "📁 上传CSV文件": if "transaction" in st.session_state: transaction = st.session_state.transaction else: st.warning("⚠️ 请先上传CSV文件并选择交易") st.stop() else: transaction = [ time, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, amount ] with st.spinner("正在分析交易..."): result = agent.process_transaction(transaction) st.success("分析完成!") col5, col6, col7 = st.columns(3) with col5: st.metric( label="预测类别", value=result.evaluation.class_name.value, delta=f"置信度: {result.evaluation.confidence.value}" ) with col6: fraud_prob = result.evaluation.fraud_probability * 100 st.metric( label="欺诈概率", value=f"{fraud_prob:.2f}%", delta=f"{100 - fraud_prob:.2f}% 正常" ) with col7: st.metric( label="行动建议数量", value=len(result.action_plan.actions), delta="已生成" ) st.divider() tab1, tab2, tab3 = st.tabs(["📊 评估结果", "🔍 特征解释", "📋 行动计划"]) with tab1: st.subheader("模型评估结果") eval_col1, eval_col2 = st.columns(2) with eval_col1: st.info(f""" **预测信息** - 预测类别: {result.evaluation.class_name.value} - 预测标签: {result.evaluation.predicted_class} - 置信度: {result.evaluation.confidence.value} """) with eval_col2: st.info(f""" **概率分布** - 欺诈概率: {result.evaluation.fraud_probability:.4f} - 正常概率: {result.evaluation.normal_probability:.4f} """) if result.evaluation.class_name == TransactionClass.FRAUD: st.error(f"⚠️ 该交易被识别为**欺诈交易**,请立即采取行动!") else: st.success(f"✅ 该交易被识别为**正常交易**") with tab2: st.subheader("🔍 特征解释") st.markdown("""

💡 什么是特征解释?

就像医生看病时会检查各项指标一样,我们的AI模型也通过分析交易的各项"特征"来判断是否为欺诈。 下面这些特征是影响判断结果最重要的因素,让我们来看看它们是如何"告诉"模型这个交易是否有问题的。

""", unsafe_allow_html=True) st.info(f""" **使用的模型**: {result.explanation.model_type} **整体判断依据**: {result.explanation.overall_explanation} """) st.write("### 📊 关键影响因素分析") st.caption("这些特征对判断结果影响最大,就像破案时的关键线索") feature_descriptions = { "Time": "交易发生的时间距离第一次交易经过的秒数。欺诈交易往往在特定时间段更频繁,比如深夜或节假日。", "Amount": "交易金额。异常高额或异常低额的交易都可能引起怀疑,特别是与用户历史消费习惯不符时。", "V1": "经过PCA(主成分分析)转换后的特征1,代表交易数据的某种模式。PCA将原始数据转换成更易分析的形式。", "V2": "经过PCA转换后的特征2,捕捉交易数据的另一种模式。", "V3": "经过PCA转换后的特征3,反映交易数据的特定维度。", "V4": "经过PCA转换后的特征4,可能代表交易频率或模式。", "V5": "经过PCA转换后的特征5,可能涉及交易的时间或空间特征。", "V6": "经过PCA转换后的特征6,可能反映交易的某种统计特性。", "V7": "经过PCA转换后的特征7,可能代表交易的异常程度。", "V8": "经过PCA转换后的特征8,可能涉及交易的上下文信息。", "V9": "经过PCA转换后的特征9,可能反映交易的时间序列特征。", "V10": "经过PCA转换后的特征10,可能代表交易的某种模式。", "V11": "经过PCA转换后的特征11,可能涉及交易的频率特征。", "V12": "经过PCA转换后的特征12,可能反映交易的异常模式。", "V13": "经过PCA转换后的特征13,可能代表交易的某种统计特性。", "V14": "经过PCA转换后的特征14,可能涉及交易的时间特征。", "V15": "经过PCA转换后的特征15,可能反映交易的某种模式。", "V16": "经过PCA转换后的特征16,可能代表交易的异常程度。", "V17": "经过PCA转换后的特征17,可能涉及交易的上下文信息。", "V18": "经过PCA转换后的特征18,可能反映交易的时间序列特征。", "V19": "经过PCA转换后的特征19,可能代表交易的某种模式。", "V20": "经过PCA转换后的特征20,可能涉及交易的频率特征。", "V21": "经过PCA转换后的特征21,可能反映交易的异常模式。", "V22": "经过PCA转换后的特征22,可能代表交易的某种统计特性。", "V23": "经过PCA转换后的特征23,可能涉及交易的时间特征。", "V24": "经过PCA转换后的特征24,可能反映交易的某种模式。", "V25": "经过PCA转换后的特征25,可能代表交易的异常程度。", "V26": "经过PCA转换后的特征26,可能涉及交易的上下文信息。", "V27": "经过PCA转换后的特征27,可能反映交易的时间序列特征。", "V28": "经过PCA转换后的特征28,可能代表交易的某种模式。" } for i, feature in enumerate(result.explanation.key_features, 1): importance_percent = min(feature.importance * 100, 100) impact_emoji = "📈" if feature.impact == "正向影响" else "📉" impact_color = "#ff5252" if feature.impact == "正向影响" else "#4caf50" feature_desc = feature_descriptions.get(feature.feature_name, "该特征是经过PCA转换后的数据特征,用于帮助模型识别交易模式。") with st.expander(f"{i}. {feature.feature_name} {impact_emoji}"): st.markdown(f"""

影响程度:

{importance_percent:.1f}% 的影响力

影响方向: {feature.impact}

📖 这个特征是什么?
{feature_desc}

💡 简单来说: {"这个特征让模型更倾向于认为这是欺诈交易" if feature.impact == "正向影响" else "这个特征让模型更倾向于认为这是正常交易"}

""", unsafe_allow_html=True) with tab3: st.subheader("📋 行动计划") st.markdown("""

🎯 为什么需要行动计划?

根据检测结果,我们为您准备了具体的行动建议。这些建议按照紧急程度排序, 帮助您快速、有效地应对可能的风险。请根据实际情况选择合适的处理方式。

""", unsafe_allow_html=True) st.write("### 🚀 建议采取的行动") st.caption("按优先级排序,从高到低依次处理") for action in result.action_plan.actions: priority_info = { Priority.URGENT: { "emoji": "🔴", "color": "#d32f2f", "bg_color": "#ffcdd2", "description": "紧急 - 需要立即处理,不能拖延" }, Priority.HIGH: { "emoji": "🟠", "color": "#f57c00", "bg_color": "#ffe0b2", "description": "高优先级 - 尽快处理" }, Priority.MEDIUM: { "emoji": "🟡", "color": "#fbc02d", "bg_color": "#fff9c4", "description": "中等优先级 - 适时处理" }, Priority.LOW: { "emoji": "🟢", "color": "#388e3c", "bg_color": "#c8e6c9", "description": "低优先级 - 可以稍后处理" }, Priority.ROUTINE: { "emoji": "⚪", "color": "#757575", "bg_color": "#e0e0e0", "description": "常规 - 按正常流程处理" } }.get(action.priority, { "emoji": "⚪", "color": "#757575", "bg_color": "#e0e0e0", "description": "常规" }) with st.container(): st.markdown(f"""
{priority_info['emoji']}

{action.action}

优先级: {action.priority.value} - {priority_info['description']}

💡 为什么要这样做?
{action.reason}

""", unsafe_allow_html=True) st.divider() st.header("📝 使用说明") with st.expander("查看使用说明"): st.markdown(""" ### 如何使用本系统 #### 方式1:上传CSV文件 1. **上传文件**: 点击"选择CSV文件"按钮上传包含交易数据的CSV文件 2. **查看数据**: 系统会显示数据预览 3. **选择交易**: 输入行号选择要分析的交易 4. **加载数据**: 点击"加载选中的交易"按钮 5. **开始检测**: 点击"检测欺诈"按钮开始分析 #### 方式2:手动输入 1. **输入特征**: 在表单中输入30个特征值 - Time: 交易发生时间(秒) - V1-V28: PCA转换后的特征 - Amount: 交易金额 2. **点击检测**: 点击"检测欺诈"按钮开始分析 3. **查看结果**: 系统会返回三个部分的结果 - **评估结果**: 模型的预测类别和概率 - **特征解释**: 影响预测的关键特征 - **行动计划**: 建议的处理措施 ### CSV文件格式要求 CSV文件必须包含以下列: - Time, V1, V2, V3, V4, V5, V6, V7, V8, V9 - V10, V11, V12, V13, V14, V15, V16, V17, V18, V19 - V20, V21, V22, V23, V24, V25, V26, V27, V28, Amount - Class (可选,如果存在会被自动删除) ### 系统特点 - ✅ 使用随机森林模型进行预测 - ✅ 支持CSV文件批量处理 - ✅ 提供特征重要性分析 - ✅ 根据置信度生成行动建议 - ✅ 实时分析,快速响应 ### 注意事项 - 本系统仅供演示使用 - 实际应用中需要结合人工审核 - 建议定期更新模型以保持准确性 """)