简介:这份资源是面向高校计算机相关专业毕业设计的完整项目包,主题为基于Hadoop的信贷风险评估数据可视化分析与预测系统,适合正在准备毕设、需要大数据与Java全栈实战案例的学生及开发者参考。项目以Java为核心,融合Hadoop大数据处理、Spring Boot框架与MySQL数据库,覆盖用户管理、风控专员、贷款信息、贷款申请、信用评估、信贷数据可视化及轮播图管理等模块,业务链路较为完整。压缩包共550个文件,约23.79MB,包含107个java源码、70个vue前端组件、57个js脚本、159个svg图标及jpg、png等图片素材,另有sql建表脚本、yml配置、xml映射、bat与cmd启动脚本、doc论文文档和pptx答辩材料,源码、论文与演示材料一并提供。目前已有96人学习下载。读者可据此快速理解信贷风控系统的整体架构与模块划分,参考前后端代码组织方式、数据库设计与可视化实现思路,并借助论文与PPT梳理毕设写作与答辩要点,减少从零搭建的时间成本。
1. 从一份信贷风控毕设包说起:Hadoop 到底在这类系统里扛了什么活
很多人第一次看到“基于 Hadoop 的信贷风险评估数据可视化分析与预测系统”这个标题,第一反应是:不就是个毕业设计吗,能有多复杂。但真动手做过信贷风控方向的人会告诉你,这个题目的坑不在算法,而在数据链路——信贷数据天生就是多源、脏、量大、字段口径不统一,单机 pandas 跑到几十万行就开始喘,更别说还要做特征回溯和批量评分。Hadoop 在这里的价值不是“为了用而用”,而是它提供了 HDFS 做原始信贷流水和征信快照的廉价存储,MapReduce 或 Spark 做离线特征聚合,最后把结果推给可视化层和预测模型。这套组合在银行消金、小贷、助贷机构的离线风控批处理里是真实存在的,不是学校里的玩具。如果你正在做这个方向的毕设,或者想拿它当跳板理解企业级信贷风控的数据流,下面这套从环境搭建到模型落地再到可视化呈现的路径,是我自己踩过一遍后觉得最稳的走法。
2. Hadoop 伪分布式环境搭建:从零到能跑通一个信贷数据清洗任务
2.1 为什么毕设阶段伪分布式就够了,以及选型理由
先把这个说清楚:信贷风险评估系统的毕设,核心考察点是“你能不能把数据从原始层搬到分析层再搬到展示层”,而不是“你能不能维护一个 50 节点的生产集群”。伪分布式模式在一台机器上模拟 NameNode、DataNode、ResourceManager、NodeManager 全部角色,HDFS 和 YARN 的行为和真集群一致,代码不用改就能迁移。我一般会建议用 Hadoop 3.x 系列,配合 Java 8,因为大量现成的教学资料和 jar 包兼容性最好。虚拟机给 4 核 8G 起步,磁盘至少 60G,因为信贷数据做特征聚合时中间结果会膨胀。网络热词里常出现的“hadoop 伪分布式搭建”和“如何在虚拟机上安装 hadoop”,本质就是这一节要解决的事。选型上不推荐一上来就搞 HA 或者 ZooKeeper 整合,那是集群运维的范畴,毕设周期内把伪分布式跑稳、把信贷数据清洗链路走通,性价比高得多。
2.2 最小可复现的安装与配置命令
下面这套命令是在 CentOS 7 或 Ubuntu 20.04 上验证过的,路径按自己习惯改,但目录结构建议保持一致,后面改配置文件时不容易乱。
# 创建 hadoop 用户并配置 sudo,避免直接用 root 跑 sudo useradd -m hadoop -s /bin/bash sudo passwd hadoop sudo usermod -aG sudo hadoop # 安装 SSH 并配置免密,伪分布式必须能免密登录 localhost su - hadoop ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys ssh localhost # 验证免密,能进去就 exit # 下载并解压 Hadoop,这里以 3.3.6 为例,具体版本按你拿到的包来 cd /opt sudo wget https://archive.apache.org/dist/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz sudo tar -zxvf hadoop-3.3.6.tar.gz sudo chown -R hadoop:hadoop /opt/hadoop-3.3.6解压完别急着启动,先把 Java 环境变量和 Hadoop 自己的环境变量写进~/.bashrc,否则后面hadoop命令会报找不到 JAVA_HOME。
# 编辑 ~/.bashrc,追加以下内容 export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 export HADOOP_HOME=/opt/hadoop-3.3.6 export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin export HDFS_NAMENODE_USER=hadoop export HDFS_DATANODE_USER=hadoop export HDFS_SECONDARYNAMENODE_USER=hadoop export YARN_RESOURCEMANAGER_USER=hadoop export YARN_NODEMANAGER_USER=hadoop source ~/.bashrc java -version # 确认 Java 8 生效接下来改四个核心配置文件,这是伪分布式能不能起来的关键。core-site.xml指定 HDFS 的默认文件系统和临时目录,hdfs-site.xml设副本数为 1,mapred-site.xml指定用 YARN 跑 MapReduce,yarn-site.xml配 ResourceManager 和 NodeManager 的通信地址。
<!-- core-site.xml --> <configuration> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/opt/hadoop-3.3.6/tmp</value> </property> </configuration> <!-- hdfs-site.xml --> <configuration> <property> <name>dfs.replication</name> <value>1</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/opt/hadoop-3.3.6/tmp/dfs/name</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/opt/hadoop-3.3.6/tmp/dfs/data</value> </property> </configuration> <!-- mapred-site.xml --> <configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> </configuration> <!-- yarn-site.xml --> <configuration> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.resourcemanager.hostname</name> <value>localhost</value> </property> </configuration>配置写完后,第一次启动前必须格式化 NameNode,然后启动 HDFS 和 YARN,用jps检查进程是否齐全。
hdfs namenode -format # 只执行一次,重复格式化会清空元数据 start-dfs.sh start-yarn.sh jps # 期望看到 NameNode、DataNode、SecondaryNameNode、ResourceManager、NodeManager逻辑说明:hdfs namenode -format会在dfs.namenode.name.dir下生成初始元数据,如果这个目录里已经有数据,格式化会直接清掉,所以生产环境绝对不能在运行的集群上执行。start-dfs.sh读取HDFS_NAMENODE_USER等环境变量,用 SSH 免密登录到 localhost 拉起进程。jps是排查启动问题最直接的工具,缺哪个进程就去翻对应的日志,日志在$HADOOP_HOME/logs下。参数上,dfs.replication在伪分布式下必须设 1,设 3 会因为只有一个 DataNode 而一直处于副本不足状态,虽然不影响读写,但会刷警告日志。
2.3 把信贷 CSV 灌进 HDFS 并跑通第一个清洗任务
环境起来后,先建目录、传数据。信贷原始数据常见的是 CSV 或从数据库导出的文本,字段包括用户 ID、申请时间、授信金额、逾期天数、征信查询次数等。
# 在 HDFS 上建信贷数据的原始层目录 hdfs dfs -mkdir -p /credit/raw hdfs dfs -mkdir -p /credit/cleaned # 把本地信贷 CSV 上传,假设文件叫 credit_apply.csv hdfs dfs -put /home/hadoop/data/credit_apply.csv /credit/raw/ # 确认上传成功 hdfs dfs -ls /credit/raw/ hdfs dfs -cat /credit/raw/credit_apply.csv | head -5清洗任务用 MapReduce 写一个最简单的过滤器:丢掉字段数不对的行、丢掉申请金额为负的行、把逾期天数标准化成 0/1 标签。下面是一个可编译的 Mapper 骨架,Reducer 直接用默认的即可。
// CreditCleanMapper.java import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import java.io.IOException; public class CreditCleanMapper extends Mapper<LongWritable, Text, Text, Text> { @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); // 跳过表头 if (line.startsWith("user_id")) return; String[] fields = line.split(","); // 字段数校验,信贷表一般 10 列以上,少于 8 列直接丢 if (fields.length < 8) return; try { double applyAmt = Double.parseDouble(fields[3]); int overdueDays = Integer.parseInt(fields[6]); // 申请金额为负或超过 100 万视为脏数据 if (applyAmt < 0 || applyAmt > 1000000) return; // 逾期天数转标签,30 天以上记 1 String label = overdueDays >= 30 ? "1" : "0"; context.write(new Text(fields[0]), new Text(label + "," + applyAmt)); } catch (NumberFormatException e) { // 数字解析失败直接丢弃,不中断任务 } } }逻辑说明:Mapper 的输入 key 是行偏移量,value 是一行文本。这里做了三层过滤——表头、字段数、数值合法性,任何一层不过就return,不写 context。参数上,applyAmt的上限 1000000 是根据常见信贷产品设的,如果你的数据集是房贷可以调高。overdueDays >= 30这个阈值对应“M1 逾期”的口径,做二分类标签时常用。编译打包后用hadoop jar提交,输出目录不能预先存在,否则任务会直接失败。
hadoop jar credit-clean.jar CreditCleanDriver /credit/raw /credit/cleaned hdfs dfs -ls /credit/cleaned hdfs dfs -cat /credit/cleaned/part-r-00000 | head -10到这一步,你已经有了一个能跑的 Hadoop 伪分布式环境,并且把信贷原始数据清洗成了带标签的样本。后面做特征聚合、模型训练和可视化,都从这个/credit/cleaned目录出发。
3. 信贷特征工程与预测模型:在 Hadoop 上做离线聚合再喂给分类器
3.1 为什么特征聚合要放在 Hadoop 侧而不是拉回单机
信贷风控模型的效果,七成靠特征。原始申请表单只有几十个字段,但真正有区分度的往往是聚合特征:近 3 个月查询次数、近 6 个月最大逾期天数、历史授信总额、负债收入比等。这些特征需要按用户 ID 做 group by 再算统计量,数据量一旦上到百万级,单机 pandas 的 groupby 会吃光内存。放在 Hadoop 侧用 MapReduce 或 Spark 做聚合,输出的是每个用户一行的宽表,体量缩小一到两个数量级,再拉回单机训练模型就轻松了。这也是企业里离线特征平台的常见做法:HDFS 存明细,Spark 算特征,特征表落 Hive 或 HDFS,模型训练读特征表。
3.2 用 MapReduce 做用户级特征聚合的完整代码
下面这个任务按用户 ID 聚合,输出查询次数、平均申请金额、最大逾期天数三个特征。Mapper 输出<user_id, query_count|apply_amt|overdue_days>,Reducer 累加。
// FeatureAggMapper.java import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import java.io.IOException; public class FeatureAggMapper extends Mapper<LongWritable, Text, Text, Text> { @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { // 输入格式:user_id,label,apply_amt,query_count,overdue_days String[] f = value.toString().split(","); if (f.length < 5) return; String userId = f[0]; String queryCount = f[3]; String applyAmt = f[2]; String overdueDays = f[4]; context.write(new Text(userId), new Text(queryCount + "|" + applyAmt + "|" + overdueDays)); } }// FeatureAggReducer.java import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer; import java.io.IOException; public class FeatureAggReducer extends Reducer<Text, Text, Text, Text> { @Override protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException { int totalQuery = 0; double totalAmt = 0; int maxOverdue = 0; int count = 0; for (Text v : values) { String[] parts = v.toString().split("\\|"); totalQuery += Integer.parseInt(parts[0]); totalAmt += Double.parseDouble(parts[1]); int od = Integer.parseInt(parts[2]); if (od > maxOverdue) maxOverdue = od; count++; } double avgAmt = count > 0 ? totalAmt / count : 0; // 输出:user_id, total_query, avg_amt, max_overdue context.write(key, new Text(totalQuery + "," + String.format("%.2f", avgAmt) + "," + maxOverdue)); } }逻辑说明:Mapper 把每行拆成三个数值用竖线拼成字符串传给 Reducer,Reducer 按 key 分组后遍历 values 做累加和取最大。参数上,totalQuery是累加值,如果你想要“近 3 个月”的查询次数,需要在 Mapper 里先按申请时间过滤,只保留时间窗口内的记录。avgAmt用String.format保留两位小数,避免输出一长串浮点尾数。这个任务的输出就是用户级特征宽表,可以直接作为模型输入。
hadoop jar feature-agg.jar FeatureAggDriver /credit/cleaned /credit/features hdfs dfs -cat /credit/features/part-r-00000 | head -103.3 把特征表拉回单机训练逻辑回归并评估
特征表通常只有几万到几十万行,拉回单机用 scikit-learn 训练完全够用。下面这段 Python 从 HDFS 拉取特征文件,训练逻辑回归,输出 AUC 和混淆矩阵。
# train_credit_model.py import subprocess import pandas as pd from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score, confusion_matrix from sklearn.preprocessing import StandardScaler # 从 HDFS 拉取特征文件到本地 subprocess.run(["hdfs", "dfs", "-get", "/credit/features/part-r-00000", "./features.csv"]) # 读取特征,列名按 Reducer 输出顺序定义 df = pd.read_csv("./features.csv", header=None, names=["user_id", "total_query", "avg_amt", "max_overdue"]) # 标签需要从清洗结果里关联,这里假设已有一列 label 合并进来 # 实际项目中用 user_id 做 join,这里用 max_overdue 模拟标签 df["label"] = (df["max_overdue"] >= 30).astype(int) X = df[["total_query", "avg_amt", "max_overdue"]] y = df["label"] # 标准化,逻辑回归对量纲敏感 scaler = StandardScaler() X_scaled = scaler.fit_transform(X) X_train, X_test, y_train, y_test = train_test_split( X_scaled, y, test_size=0.3, random_state=42, stratify=y) model = LogisticRegression(class_weight="balanced", max_iter=1000) model.fit(X_train, y_train) y_prob = model.predict_proba(X_test)[:, 1] y_pred = model.predict(X_test) print("AUC:", roc_auc_score(y_test, y_prob)) print("混淆矩阵:\n", confusion_matrix(y_test, y_pred))逻辑说明:subprocess.run调用hdfs dfs -get把特征文件拉到本地,这是 Hadoop 和单机 Python 之间最直接的桥接方式。StandardScaler对三个特征做标准化,因为avg_amt的量级可能是几万,而total_query只有几十,不标准化会导致逻辑回归收敛慢甚至不收敛。class_weight="balanced"处理信贷场景常见的样本不平衡——逾期样本通常远少于正常样本。参数上,test_size=0.3是常规划分,stratify=y保证训练集和测试集的标签比例一致。AUC 在 0.7 以上说明特征有一定区分度,低于 0.65 就要回头检查特征工程。
4. 数据可视化与预测结果呈现:ECharts 对接 Hadoop 分析结果的落地方式
4.1 可视化层的数据接口怎么设计才不返工
可视化最容易翻车的地方不是图表画不出来,而是数据接口反复改。我的经验是:Hadoop 侧的输出直接落成 JSON 或 CSV,前端只认一种格式,中间不加额外的转换层。具体来说,特征聚合结果和模型评分结果分别导出成两个文件,前端用 ECharts 读取后渲染。信贷风控看板通常需要四类图:逾期率分布直方图、申请金额箱线图、特征重要性条形图、用户评分分布饼图。这些图的数据源都来自 HDFS 上的聚合结果,不要在前端做二次计算,否则数据量一大浏览器就卡死。
4.2 用 Python 把 HDFS 结果转成 ECharts 可用的 JSON
下面这段脚本从 HDFS 读取特征表,统计逾期率分布和金额分箱,输出一个前端可直接 fetch 的 JSON 文件。
# export_chart_data.py import json import subprocess import pandas as pd subprocess.run(["hdfs", "dfs", "-get", "/credit/features/part-r-00000", "./features.csv"]) df = pd.read_csv("./features.csv", header=None, names=["user_id", "total_query", "avg_amt", "max_overdue"]) df["label"] = (df["max_overdue"] >= 30).astype(int) # 逾期率按查询次数分箱 df["query_bin"] = pd.cut(df["total_query"], bins=[0, 5, 10, 20, 50, 1000], labels=["0-5", "6-10", "11-20", "21-50", "50+"]) overdue_rate = df.groupby("query_bin")["label"].mean().reset_index() overdue_rate.columns = ["bin", "rate"] # 申请金额分箱统计 df["amt_bin"] = pd.cut(df["avg_amt"], bins=[0, 5000, 20000, 50000, 100000, 1000000], labels=["<5k", "5k-20k", "20k-50k", "50k-100k", ">100k"]) amt_dist = df.groupby("amt_bin").size().reset_index(name="count") chart_data = { "overdue_rate": { "x": overdue_rate["bin"].tolist(), "y": [round(v, 4) for v in overdue_rate["rate"].tolist()] }, "amt_dist": { "x": amt_dist["amt_bin"].tolist(), "y": amt_dist["count"].tolist() } } with open("./chart_data.json", "w", encoding="utf-8") as f: json.dump(chart_data, f, ensure_ascii=False, indent=2) print("导出完成,字段:", list(chart_data.keys()))逻辑说明:pd.cut把连续变量分箱,这是风控看板最常用的展示方式,因为业务方看的是区间而不是具体值。groupby("query_bin")["label"].mean()直接算出每个查询次数区间的逾期率,比手写循环快得多。输出的 JSON 结构固定为{x: [], y: []},前端 ECharts 的xAxis.data和series.data直接对应,不需要额外适配。参数上,分箱边界要根据你的实际数据分布调整,如果大部分用户的查询次数在 10 以内,bins就要设得更细。
4.3 ECharts 前端渲染的最小页面
前端只需要一个 HTML 文件加 ECharts 的 CDN 引用,从本地或后端接口读取chart_data.json即可。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script> </head> <body> <div id="overdueChart" style="width:600px;height:400px;"></div> <div id="amtChart" style="width:600px;height:400px;"></div> <script> fetch('./chart_data.json') .then(res => res.json()) .then(data => { const chart1 = echarts.init(document.getElementById('overdueChart')); chart1.setOption({ title: { text: '不同查询次数区间的逾期率' }, xAxis: { type: 'category', data: data.overdue_rate.x }, yAxis: { type: 'value', name: '逾期率' }, series: [{ type: 'bar', data: data.overdue_rate.y }] }); const chart2 = echarts.init(document.getElementById('amtChart')); chart2.setOption({ title: { text: '申请金额分布' }, xAxis: { type: 'category', data: data.amt_dist.x }, yAxis: { type: 'value', name: '人数' }, series: [{ type: 'bar', data: data.amt_dist.y }] }); }); </script> </body> </html>逻辑说明:fetch读取同目录下的 JSON 文件,echarts.init绑定 DOM 容器,setOption里xAxis.data和series.data分别对应 Python 导出的x和y数组。参数上,type: 'bar'是柱状图,逾期率也可以用'line'画折线,看趋势更直观。如果数据量大,fetch要改成后端接口分页拉取,不要一次性加载几十万条。
5. 避坑与排查:信贷风控毕设里最容易翻车的五个地方
5.1 现象:NameNode 启动后秒退,jps 里看不到进程
原因通常是dfs.namenode.name.dir指向的目录权限不对,或者重复执行了hdfs namenode -format导致 clusterID 不一致。解决方法是先看$HADOOP_HOME/logs下 NameNode 的日志,找到具体报错行。如果是权限问题,chown -R hadoop:hadoop把目录改对;如果是 clusterID 不一致,把dfs.namenode.name.dir和dfs.datanode.data.dir下的current目录全部删掉,重新格式化一次。注意格式化只能做一次,做完后不要再执行。
5.2 现象:MapReduce 任务卡在 map 0% reduce 0% 不动
原因多半是 YARN 的 NodeManager 没起来,或者mapred-site.xml里mapreduce.framework.name没设成yarn。先jps确认 NodeManager 在不在,不在就去看yarn-site.xml里yarn.nodemanager.aux-services是否配了mapreduce_shuffle。另一个常见原因是内存不够,yarn.nodemanager.resource.memory-mb默认值偏小,在 8G 虚拟机上可以调到 4096,同时把mapreduce.map.memory.mb和mapreduce.reduce.memory.mb设成 1024。
5.3 现象:信贷 CSV 上传 HDFS 后中文乱码
原因是本地文件编码是 GBK,HDFS 不关心编码,但后续 MapReduce 按 UTF-8 读就会乱。解决方法是上传前用iconv -f GBK -t UTF-8 credit_apply.csv > credit_apply_utf8.csv转码,再hdfs dfs -put。如果已经传了,拉回来转码再传一次。这个坑在征信数据里特别常见,因为很多导出工具默认 GBK。
5.4 现象:模型 AUC 很高但上线一塌糊涂
这是典型的特征穿越。比如你用“最大逾期天数”作为特征去预测“是否逾期”,而最大逾期天数本身就是逾期之后才产生的字段,模型在训练集上当然准,实际申请时这个字段根本拿不到。解决方法是把所有特征按“申请时点是否可得”过一遍,只保留申请前就能采集到的字段,比如历史查询次数、历史授信金额。这个坑我见过太多人踩,血泪经验就是:特征表做完后,逐列问自己“这个值在用户提交申请那一刻存在吗”。
5.5 现象:ECharts 图表空白,控制台报 CORS 错误
原因是直接双击打开 HTML 文件,fetch本地 JSON 被浏览器安全策略拦截。解决方法是起一个本地静态服务器,python -m http.server 8000,然后访问http://localhost:8000。如果 JSON 是从后端接口拉的,后端要加Access-Control-Allow-Origin头。这个坑不涉及 Hadoop,但卡住过很多前端不熟的人。
6. 把离线评分做成可复现的批处理脚本:一个让毕设答辩加分的技巧
走到这里,你已经有了环境、特征、模型和可视化。但答辩时老师最容易问的是:“你这个系统怎么跑起来?”如果你只能一步步敲命令,印象分会打折扣。我的习惯是把整条链路写成一个 shell 脚本,从 HDFS 拉数据、跑聚合、训练模型、导出 JSON,一条命令跑完。下面这个脚本就是干这个的。
#!/bin/bash # run_credit_pipeline.sh set -e # 任何一步失败就退出,避免脏数据往下传 echo "=== 1. 上传原始信贷数据到 HDFS ===" hdfs dfs -rm -r /credit/raw || true hdfs dfs -mkdir -p /credit/raw hdfs dfs -put /home/hadoop/data/credit_apply.csv /credit/raw/ echo "=== 2. 跑清洗任务 ===" hdfs dfs -rm -r /credit/cleaned || true hadoop jar credit-clean.jar CreditCleanDriver /credit/raw /credit/cleaned echo "=== 3. 跑特征聚合任务 ===" hdfs dfs -rm -r /credit/features || true hadoop jar feature-agg.jar FeatureAggDriver /credit/cleaned /credit/features echo "=== 4. 训练模型并导出图表数据 ===" python3 train_credit_model.py python3 export_chart_data.py echo "=== 5. 启动可视化页面 ===" python3 -m http.server 8000 & echo "看板地址:http://localhost:8000/index.html"逻辑说明:set -e是整条脚本的后悔药,任何一步返回非零就停,不会带着错误结果继续跑。hdfs dfs -rm -r ... || true保证目录不存在时也不报错退出,因为 Hadoop 的输出目录必须不存在才能写入。最后用python3 -m http.server起静态服务,避免 CORS 问题。参数上,credit_apply.csv的路径和 jar 包名字按你自己的项目改,但脚本结构可以直接抄。
这个脚本的价值在于:答辩演示时你只需要跑一条命令,所有中间结果和最终看板自动生成,老师问“数据怎么流的”你就把脚本打开,从 HDFS 到清洗到特征到模型到可视化,链路一目了然。我自己的习惯是每次改完特征或模型,先跑一遍这个脚本确认端到端没问题,再去看具体指标。另外一个小技巧:在脚本最后加一行date,记录本次运行时间,方便对比不同版本的效果。做毕设和做线上系统一样,可复现比单次跑通重要得多。希望帮到你。
本文还有配套的精品资源,点击获取