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模型也通过分析交易的各项"特征"来判断是否为欺诈。 下面这些特征是影响判断结果最重要的因素,让我们来看看它们是如何"告诉"模型这个交易是否有问题的。
影响程度:
{importance_percent:.1f}% 的影响力
影响方向: {feature.impact}
📖 这个特征是什么?
{feature_desc}
💡 简单来说: {"这个特征让模型更倾向于认为这是欺诈交易" if feature.impact == "正向影响" else "这个特征让模型更倾向于认为这是正常交易"}
根据检测结果,我们为您准备了具体的行动建议。这些建议按照紧急程度排序, 帮助您快速、有效地应对可能的风险。请根据实际情况选择合适的处理方式。
优先级: {action.priority.value} - {priority_info['description']}
💡 为什么要这样做?
{action.reason}