简介这份资源是面向计算机相关专业学生的机器学习与数据挖掘课程设计完整项目以米其林餐厅数据为分析对象构建了一套数据挖掘管理系统。项目经导师指导并获98分认可适合正在做课程设计、期末大作业或需要项目实战练习的学习者参考。压缩包共46个文件约292KB以18个Java源码文件为核心配合10个FreeMarker模板、5个CSS样式及XML、SQL、YML等配置另有4份CSV数据集与4张图片素材整体结构清晰便于按模块阅读与二次开发。资源已积累335人学习说明其参考价值得到一定验证。读者可获得从数据清洗、多星级餐厅数据组织到系统功能实现的完整赛题方案配套使用文档说明降低了上手门槛SQL脚本与CSV数据可直接复现分析流程Java与模板层代码则展示了数据管理系统的典型分层设计适合作为课程设计模板或实战练手项目。1. 米其林餐厅数据挖掘管理系统从课程设计到可复现的工程落地拿到「机器学习和数据挖掘课程设计-米其林餐厅数据挖掘管理系统源码使用文档说明.zip」这个题目时很多人第一反应是去搜一份现成源码跑通了事。但真正做过课程设计的人都知道能跑通和能讲清楚之间隔着一整套数据管道、特征工程和模型评估的逻辑。米其林餐厅数据本身就是一个非常适合练手的结构化数据集餐厅名称、星级、地理位置、菜系类型、价格区间、评论文本字段维度适中既有分类任务也有聚类和关联规则挖掘的空间。这篇文章面向正在做机器学习课程设计、数据挖掘实验或者想找一个完整项目练手的同学把「米其林餐厅数据挖掘管理系统」从数据获取、清洗、建模到可视化管理的完整链路拆开讲。我不会假设你手里已经有一份完美源码而是按一线做课程设计项目的常见路径把每一步的选择理由、参数设置和容易翻车的地方说清楚。读完你至少能自己搭出一套能演示、能答辩、能继续扩展的系统而不是只会改别人代码里的路径。2. 数据层设计米其林餐厅数据集怎么来、怎么洗、怎么存2.1 数据来源与字段结构米其林餐厅数据不像电商或电影数据那样有现成的 Kaggle 大礼包常见做法是组合几个来源米其林官网的公开列表页、大众点评或 Yelp 的餐厅信息、以及一些开放数据集平台上的餐饮 POI 数据。课程设计场景下我一般建议先用一份结构化的 CSV 作为起点字段至少包含餐厅名称、城市、国家、星级一星到三星以及必比登推荐、菜系分类、价格等级用符号数量表示、经纬度、评论文本、评分。如果要做文本挖掘评论文本字段是核心如果只做结构化挖掘星级和菜系就是主要标签。数据量方面课程设计不需要几十万条3000 到 8000 条就能跑出有说服力的结果。关键是字段完整度尤其是星级和菜系不能有大量缺失否则后面分类模型会直接翻车。2.2 清洗流程与代码实现拿到原始数据后第一步不是急着建模而是把脏数据处理干净。米其林数据常见的脏法包括星级字段混入了「必比登」「餐盘推荐」等非星级标签、价格等级用不同符号体系表示、城市名有中英文混用、经纬度缺失或越界。下面是一段我常用的清洗脚本基于 pandas 实现。import pandas as pd import numpy as np # 读取原始数据注意编码问题米其林数据常含特殊字符 df pd.read_csv(michelin_raw.csv, encodingutf-8-sig) # 1. 星级字段标准化只保留 1-3 星其余归为 Recommended def normalize_star(val): if pd.isna(val): return Unknown val str(val).strip() if 一星 in val or 1 star in val.lower(): return 1 Star elif 二星 in val or 2 star in val.lower(): return 2 Star elif 三星 in val or 3 star in val.lower(): return 3 Star else: return Recommended df[star_level] df[star].apply(normalize_star) # 2. 价格等级统一为 1-4 的整数 price_map {$: 1, $$: 2, $$$: 3, $$$$: 4} df[price_level] df[price].map(price_map).fillna(0).astype(int) # 3. 城市字段去空格、统一小写 df[city] df[city].str.strip().str.lower() # 4. 经纬度异常值处理纬度 -90~90经度 -180~180 df df[(df[latitude].between(-90, 90)) (df[longitude].between(-180, 180))] # 5. 评论文本去重、去空 df df.dropna(subset[review_text]) df df[df[review_text].str.len() 10] print(f清洗后数据量: {len(df)}) print(df[star_level].value_counts())这段代码的逻辑很直接先把星级这种分类标签统一成模型能吃的格式再把价格这种有序变量转成数值最后处理地理坐标的越界问题。参数上唯一需要根据你实际数据调整的是price_map如果你拿到的数据用「人均消费」数值表示那就直接分箱成四档。清洗完建议把结果存成新的 CSV 或直接写入 SQLite方便后面管理系统读取。2.3 存储选型SQLite 还是 MySQL课程设计管理系统通常需要一个小型数据库来支撑增删改查和前端展示。我的建议是如果只是本地演示和答辩SQLite 足够零配置、单文件、Python 原生支持如果要模拟真实管理系统、有多用户并发或者需要远程访问那就上 MySQL。下面是把清洗后数据写入 SQLite 的最小代码。import sqlite3 conn sqlite3.connect(michelin.db) df.to_sql(restaurants, conn, if_existsreplace, indexFalse) # 建一个索引加速按城市和星级查询 conn.execute(CREATE INDEX IF NOT EXISTS idx_city_star ON restaurants(city, star_level)) conn.commit() conn.close()提示SQLite 在并发写入时会有锁表问题课程设计演示阶段单线程操作没问题但如果前端多个请求同时写建议换 MySQL 或者加一个简单的写入队列。3. 挖掘与建模分类、聚类、关联规则三条线怎么跑3.1 星级预测用随机森林做分类的完整流程米其林数据最自然的监督学习任务就是「给定餐厅特征预测它能不能拿到星级」。这是一个二分类或多分类问题。特征可以用价格等级、菜系 one-hot 编码、城市频率编码、评论长度、评论情感得分。标签就是 star_level 是否属于 1-3 星。下面是一个可复现的随机森林流程。from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.preprocessing import LabelEncoder from sklearn.metrics import classification_report from sklearn.feature_extraction.text import TfidfVectorizer import scipy.sparse as sp # 构造标签有星级为 1推荐为 0 df[is_starred] df[star_level].apply(lambda x: 1 if Star in x else 0) # 文本特征TF-IDF 取前 500 个词 tfidf TfidfVectorizer(max_features500, stop_wordsenglish) text_features tfidf.fit_transform(df[review_text].fillna()) # 结构化特征 le_cuisine LabelEncoder() df[cuisine_enc] le_cuisine.fit_transform(df[cuisine].fillna(Unknown)) struct_features df[[price_level, cuisine_enc]].values # 合并特征 X sp.hstack([text_features, struct_features]).tocsr() y df[is_starred].values X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) clf RandomForestClassifier(n_estimators200, max_depth12, random_state42) clf.fit(X_train, y_train) y_pred clf.predict(X_test) print(classification_report(y_test, y_pred))这里有几个参数值得说清楚。max_features500是控制文本维度太大容易过拟合太小信息不够500 到 1000 之间比较稳。n_estimators200是树的数量课程设计数据量下 100 到 300 都合理再多训练时间上去了收益很小。max_depth12是防止树长得太深把训练集背下来。如果你发现准确率虚高但测试集拉胯优先降 max_depth 和减 max_features。3.2 餐厅聚类用 K-Means 找城市餐饮格局无监督部分聚类是最容易出可视化效果的。把餐厅按价格、评分、评论情感得分做 K-Means能分出「高端高价」「平价高评」「网红打卡」等群体。代码不复杂关键在 K 值怎么选。from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler from sklearn.metrics import silhouette_score # 选取聚类特征 cluster_features df[[price_level, rating, review_length]].fillna(0) scaler StandardScaler() X_scaled scaler.fit_transform(cluster_features) # 用轮廓系数选 K best_k, best_score 2, -1 for k in range(2, 8): km KMeans(n_clustersk, random_state42, n_init10) labels km.fit_predict(X_scaled) score silhouette_score(X_scaled, labels) if score best_score: best_k, best_score k, score print(f最佳聚类数: {best_k}, 轮廓系数: {best_score:.3f}) km_final KMeans(n_clustersbest_k, random_state42, n_init10) df[cluster] km_final.fit_predict(X_scaled)n_init10是跑 10 次不同初始化取最优避免陷入局部最优。轮廓系数在 0.3 以上就算结构比较清晰了低于 0.2 说明特征选得不好或者数据本身没有明显分群这时候不要硬解释聚类结果。3.3 关联规则菜系与价格档位的共现挖掘关联规则挖掘适合回答「哪些菜系经常和高价档位一起出现」这类问题。用 Apriori 或者 FP-Growth 都行课程设计里 mlxtend 库最省事。from mlxtend.frequent_patterns import apriori, association_rules import pandas as pd # 构造事务矩阵每行一个餐厅列为菜系和价格档位的组合 basket pd.get_dummies(df[cuisine].astype(str) _ df[price_level].astype(str)) basket basket.groupby(level0).max() # 同一餐厅去重 frequent_items apriori(basket, min_support0.02, use_colnamesTrue) rules association_rules(frequent_items, metriclift, min_threshold1.2) rules rules.sort_values(lift, ascendingFalse).head(20) print(rules[[antecedents, consequents, support, confidence, lift]])min_support0.02表示组合至少出现在 2% 的餐厅里数据量小就调到 0.01数据量大就调到 0.05。lift 1.2才说明两者有正相关等于 1 就是独立事件低于 1 是负相关。这一步的输出可以直接做成管理系统的「洞察面板」。4. 管理系统实现后端接口与前端展示怎么串起来4.1 用 Flask 搭最小可用后端课程设计的管理系统不需要微服务那套一个 Flask 应用加几个路由就能撑起来。核心接口包括餐厅列表分页查询、按城市和星级筛选、聚类结果展示、关联规则展示。from flask import Flask, jsonify, request import sqlite3 app Flask(__name__) def get_db(): conn sqlite3.connect(michelin.db) conn.row_factory sqlite3.Row return conn app.route(/api/restaurants) def list_restaurants(): city request.args.get(city, ) star request.args.get(star, ) page int(request.args.get(page, 1)) size int(request.args.get(size, 20)) query SELECT * FROM restaurants WHERE 11 params [] if city: query AND city ? params.append(city) if star: query AND star_level ? params.append(star) query LIMIT ? OFFSET ? params.extend([size, (page - 1) * size]) conn get_db() rows conn.execute(query, params).fetchall() conn.close() return jsonify([dict(r) for r in rows]) if __name__ __main__: app.run(debugTrue, port5000)这个接口的设计要点是分页和条件筛选都走参数化查询避免 SQL 注入。page和size的默认值让前端不传参也能拿到数据。如果你要加聚类结果就在 restaurants 表里加一个 cluster 字段查询时一起返回。4.2 前端展示用 ECharts 做聚类散点图和规则网络图前端不需要上 React 或 Vue 的重型方案一个 HTML 页面加 ECharts CDN 就能做出答辩级别的可视化。聚类结果用散点图x 轴价格、y 轴评分、颜色区分 cluster关联规则用关系图节点是菜系和价格档位边是 lift 值。// 聚类散点图配置 const scatterOption { title: { text: 餐厅聚类分布 }, xAxis: { name: 价格等级, type: value }, yAxis: { name: 评分, type: value }, series: [{ type: scatter, symbolSize: 8, data: clusterData.map(d [d.price_level, d.rating, d.cluster]), encode: { color: 2 } }] };ECharts 的encode.color可以直接把 cluster 字段映射到颜色省去手动分组。数据从后端/api/restaurants拿前端用 fetch 请求后按 cluster 分组塞进 series 就行。注意课程设计答辩时老师常问「你的聚类结果业务上怎么解释」所以每个 cluster 要提前算好平均价格、平均评分、主要菜系做成一张描述表别只放图。5. 避坑与排查课程设计里最容易翻车的五个地方5.1 中文编码导致数据读取乱码现象用 pandas 读 CSV 时餐厅名称和评论变成乱码或者直接报 UnicodeDecodeError。原因米其林数据来源多样有的用 UTF-8有的用 GBK还有的带 BOM。解决先试encodingutf-8-sig不行换gbk再不行用chardet检测。写入数据库时统一用 UTF-8。5.2 文本特征维度爆炸拖垮训练现象加了 TF-IDF 之后训练时间从几秒变成几分钟内存直接吃满。原因没有限制 max_features评论文本分词后产生几万个特征。解决TfidfVectorizer(max_features500)或者先做卡方检验选 top 特征。课程设计数据量下500 到 1000 维足够。5.3 分类标签不平衡导致模型只会猜多数类现象准确率 85% 看起来不错但混淆矩阵里少数类全错。原因米其林数据里推荐餐厅远多于星级餐厅直接训练模型会偏向多数类。解决用class_weightbalanced或者对多数类下采样。评估时看 F1 和 AUC别只看 accuracy。5.4 聚类前没做标准化导致价格主导结果现象聚类结果全是按价格分的评分和评论长度完全没起作用。原因价格等级是 1-4评分是 0-5量纲接近但分布不同K-Means 对尺度敏感。解决聚类前必须StandardScaler把所有特征拉到同一量纲。5.5 前端请求跨域被浏览器拦截现象Flask 跑在 5000 端口前端页面直接打开 file:// 或者跑在 8080fetch 请求报 CORS 错误。原因浏览器同源策略。解决Flask 加flask-cors扩展CORS(app)一行搞定或者把前端页面也放到 Flask 的 static 目录下同源访问。6. 把课程设计变成能写进简历的项目三个进阶技巧6.1 用情感分析给评论加一列特征原始数据里的评论文本如果只用来做 TF-IDF信息利用率太低。加一列情感得分用 SnowNLP 或者 VADER 都行然后把这个得分作为结构化特征喂给分类模型。我试过在米其林数据上加情感特征后星级预测的 F1 能涨 3 到 5 个百分点。代码就三行from snownlp import SnowNLP df[sentiment] df[review_text].apply(lambda x: SnowNLP(x).sentiments if x else 0.5)注意 SnowNLP 对英文评论效果一般如果你的数据以英文为主换 VADER 更稳。6.2 把模型持久化让管理系统真正「在线预测」课程设计答辩时如果老师输入一个餐厅特征系统能实时返回预测星级效果比只展示离线图表好得多。用 joblib 把训练好的模型存下来Flask 加一个/api/predict接口。import joblib # 训练完后保存 joblib.dump(clf, star_model.pkl) joblib.dump(tfidf, tfidf.pkl) # Flask 里加载 clf joblib.load(star_model.pkl) tfidf joblib.load(tfidf.pkl) app.route(/api/predict, methods[POST]) def predict(): data request.json text_vec tfidf.transform([data[review_text]]) struct [[data[price_level], data[cuisine_enc]]] X sp.hstack([text_vec, struct]).tocsr() pred clf.predict(X)[0] return jsonify({is_starred: int(pred)})这里的关键是训练时的特征处理流程要和预测时完全一致tfidf 必须用训练集 fit 好的那个不能重新 fit。我见过有人预测时重新 fit 导致结果完全对不上排查了半天。6.3 用 Docker 把整个系统打包答辩现场不翻车课程设计最怕答辩现场环境出问题。把 Flask 应用、SQLite 数据库、前端静态文件打成一个 Docker 镜像换台机器docker run就能跑。Dockerfile 很简单FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . EXPOSE 5000 CMD [python, app.py]requirements.txt 里把 pandas、scikit-learn、flask、joblib 版本钉死避免现场装包出玄学问题。镜像构建一次大概 2 分钟之后启动就是秒级。这三个技巧里情感特征和模型持久化是性价比最高的Docker 打包属于锦上添花。我自己做课程设计时最大的教训就是别等到答辩前一天才把模型和系统串起来离线跑通和在线服务之间还有一堆编码、路径、依赖的坑。提前一周把 Flask 接口调通留足时间处理那些「本地能跑、换台机器就挂」的问题。希望帮到你。本文还有配套的精品资源点击获取