尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
米其林餐厅数据挖掘管理系统:从清洗到聚类预测的实战指南
简介面向计算机相关专业课程设计的高分项目包选题聚焦米其林餐厅数据挖掘与管理完整覆盖数据清洗、数据入库、特征分析与系统管理实现适合正在做机器学习/数据挖掘期末大作业的学生以及需要真实业务场景进行实战练习的初学者。压缩包共46个文件约292KB主体为Java源码、FreeMarker页面模板与CSS样式配合YAML配置、Maven依赖及SQL脚本构成可运行工程数据侧包含米其林一星至三星餐厅的CSV原始数据可用于对比不同星级餐厅特征理解数据预处理与挖掘流程。项目曾获导师指导并认可达到98分具备清晰的目录结构与使用文档说明是课程设计报告撰写、答辩演示和代码复现的良好参照。目前已有336人学习包体虽小但步骤完整从数据整理到结果展示均有可运行样例方便读者快速上手并迁移到自己的课题中。1. 米其林餐厅数据挖掘管理系统这门课程设计到底在做什么机器学习和数据挖掘课程设计里“米其林餐厅数据挖掘管理系统”是一个把整条数据挖掘流水线串起来的综合选题。它把米其林餐厅的公开数据作为研究对象要求完成从数据清洗、特征工程到聚类、关联规则、分类建模再到把算法封装成带界面管理系统的全过程。这个题目之所以高频出现在高校课程设计里是因为它一个题目同时覆盖了数据预处理、算法应用和系统开发三个考核点不需要硬件设备一台普通电脑就能跑通也方便老师现场追问算法的选型和参数。适合想在一周内拿出完整可演示作品、又不想在选题上冒大风险的在校学生。2. 数据从哪里来抓取、清洗与特征工程的完整落地做数据挖掘管理系统第一步不是画界面而是把“数据从哪来”定死。数据如果松松垮垮后面所有算法结论都会被质疑老师第一个问题往往就是“你清洗过没有”。米其林餐厅的开放数据常见来源有两类直接扒米其林指南公开页面或者从公开数据平台找整理好的餐厅清单。两者的差别你最好一开始就清楚前者能拿到最新评级和准确坐标但动态加载和反爬会消耗你一整天后者干净省事但字段缺失和更新滞后是常态。课程设计的正确姿势是把现成数据集当基准先跑通流程再决定要不要抓数据补充。2.1 数据源怎么选官网抓取还是现成数据集先说我自己的选择小样本、时间紧的前提下用公开数据集是性价比最高的路径。有一定知名度的米其林数据集一般包含餐厅名、城市、国家、奖项三星/二星/一星/必比登/推荐、菜系、价格档位、经纬度字段够用导入 pandas 就能开始干活。但你要在文档里写清楚数据的抓取时间和来源这关系到后面挖掘结论的有效期——米其林评级每年更新拿三年前的榜单分析现在的情况老师会挑刺。如果决定自己抓常见做法是分析米其林指南网站的 JSON 接口而不是硬啃 HTML因为页面是动态渲染的。下面是一个最小抓取示例演示把结构化字段从页面里提出来import requests from bs4 import BeautifulSoup url https://example.com/michelin/paris # 替换为实际目标页面或JSON接口 resp requests.get(url, headers{User-Agent: Mozilla/5.0}) soup BeautifulSoup(resp.text, html.parser) # 常见的页面结构每条餐厅记录在一个带餐厅ID的div里 items soup.select(div[data-restaurant]) rows [] for it in items: rows.append({ name: it.select_one(.rest-name).text.strip(), city: it.select_one(.rest-city).text.strip(), award: it.select_one(.rest-award).text.strip(), cuisine: it.select_one(.rest-cuisine).text.strip(), price: it.select_one(.rest-price).text.strip(), }) import pandas as pd df pd.DataFrame(rows) df.to_csv(michelin_raw.csv, indexFalse)代码里值得注意的两个点一是 requests 要带 User-Agent 头假装自己是浏览器否则很多站点直接返回 403二是所有字段都做了 strip() 去掉前后空白因为网页里提取的文本常常带着换行和空格不清理后面匹配就出鬼。这个示例用到的是静态 HTML 解析实际抓取时你大概率会遇到两种情况页面内容是通过 JavaScript 异步加载的或者服务端有频率限制。第一种情况去 DevTools 的 Network 面板找以 api 或 json 结尾的请求直接请求那个地址第二种情况在两次请求之间加 time.sleep(1)把抓取频率降下来。课程设计不鼓励把战线拉太长抓到几千条够用就收手。2.2 清洗落到代码去重、价格区间标准化与缺失值处理不管数据是下载的还是抓的第一版一定脏得没法直接建模。米其林数据常见的脏问题同名餐厅在不同城市各出现一次导致重复价格用“€€€€”这样的符号表示算法不认识奖项字段值是“三星”“一星”这样的中文需要转数字城市大小写不统一“Paris”和“paris”会被当成两个地方。下面这条清洗流程把这些问题一次处理完import pandas as pd import numpy as np df pd.read_csv(michelin_raw.csv) # 1. 去重同一家餐厅以 名称城市国家 为唯一键 df df.drop_duplicates(subset[name, city, country]) print(f去重后剩余 {df.shape[0]} 条) # 2. 价格区间映射€€€€ 转成 1~4 的数值 price_map {€: 1, €€: 2, €€€: 3, €€€€: 4} df[price_level] df[price].map(price_map) # 3. 奖项转星级三星3二星2一星1其余0 df[star_rating] df[award].map( {三星: 3, 二星: 2, 一星: 1}).fillna(0).astype(int) # 4. 缺失值价格或城市缺失的餐厅直接删除 df df.dropna(subset[price_level, country]) # 5. 城市名统一避免大小写分裂 df[city] df[city].str.strip().str.title() df.to_csv(michelin_clean.csv, indexFalse)这里的顺序是有讲究的去重要放在最前面因为后面做的标准化都会改变原始字段一旦做错想回滚就麻烦。第 2 步的 map 是硬映射遇到字典里没有的值会变成 NaN后面的 dropna 会兜底所以不用担心漏网之鱼。第 3 步把“必比登”“推荐”“入选”这类奖项统一归 0意思是“没有星级”这在后面的二分类任务里会很有用要注意的是这个映射会丢失奖项间的细微差别如果你后续打算单独研究必比登餐厅就把它单独编成 1 而不是 0。第 4 步直接删除缺失行在几百条到上千条的数据规模下删几个缺失坐标或缺失价格的行对整体结论影响很小比花时间做插值更务实。插值更适合连续数值型的大数据场景课程设计里强行插值反而让老师觉得你小题大做。drop_duplicates 的 subset 参数要写明白默认是全列重复才算重复不写 subset 会把“碰巧所有字段都相同的不同餐厅”也删掉。fillna(0).astype(int) 是承接前面的 map 结果把映射缺失的 NaN 填成 0表示这家餐厅没有获得星级。城市名用 str.title() 把“new york”变成“New York”真实数据里如果有类似“Mc Donald”这种被误转的情况还需要自己维护一份城市改名映射表课程设计里一般不会遇到。2.3 特征工程把菜系、城市变成模型能吃的东西清洗完的数据能查能看但机器学习模型只吃数值。特征工程这一步要做三件事把菜系文本变成 one-hot 向量、构造城市级的聚合特征、决定用什么当标签。直接贴代码# 1. 菜系one-hot出现次数少于5次的菜系合并为 Other防止特征列爆炸 cuisine_count df[cuisine].value_counts() keep_cuisines cuisine_count[cuisine_count 5].index df[cuisine_main] df[cuisine].where(df[cuisine].isin(keep_cuisines), Other) # 2. 城市级聚合特征一个城市有多少家米其林餐厅、平均价格水平 city_stats df.groupby(city).agg( city_count(name, count), city_avg_price(price_level, mean) ).reset_index() df df.merge(city_stats, oncity, howleft) # 3. 构造建模用的特征矩阵 cuisine_dummies pd.get_dummies(df[cuisine_main], prefixcuisine) features pd.concat([ df[[price_level, city_count, city_avg_price]], cuisine_dummies ], axis1) # 4. 标签是否获得星级二分类 df[has_star] (df[star_rating] 1).astype(int)第 1 步的阈值 5 不是拍脑袋定的。米其林数据集里菜系分布极不均衡法餐可能几百条某个冷门菜系只有一两条如果全保留one-hot 后特征矩阵会多出几十个全是 0 的列模型学不到东西还拖慢训练。把数量少于 5 的菜系统一装进 Other既保留了高频菜系的信息又把噪声收到一个桶里。第 2 步构造的城市平均价格描述的是“这座城市整体消费水平”对“高消费城市更容易出星级餐厅”这种假设是有解释力的city_count 描述的是“米其林密度”密度越高的城市新餐厅获星概率往往也越高。第 3 步用 concat 把数值特征和 one-hot 特征拼在一起注意别把标签列也拼进去。第 4 步的关键是为什么不做星级的多分类数据里三星餐厅通常只有个位数二星也就几十条模型会严重偏向多数的“一星与无星”学不出有意义的东西。退化成“有没有星”的二分类样本比例立刻健康得多还能用多数类样本给少数类做补充训练。这在文档里写清楚答辩时不但不是弱点反而是你理解类别不平衡问题的证据。还要注意目标泄露城市平均价格是安全的但如果把“同一城市其他餐厅的星级均值”当特征模型就等于“看邻居打分”属于目标泄露。判断特征是否泄露的标准很简单预测时你能拿到这个数吗拿到就是正常的拿不到就是偷看了答案。3. 挖掘任务怎么定聚类、关联规则与星级预测的参数实践系统的价值不只是“能查数据”而是能跑挖掘任务。常见课程设计要求做两个以上挖掘任务我一般建议做三个KMeans 聚类给餐厅分群、Apriori 关联规则发现菜系与城市的共现模式、随机森林做星级预测。这三个任务覆盖了无监督、关联规则、有监督三个方向算法种类不重复还都能在几百条数据上快速出结果。3.1 先想清楚任务类型三种挖掘问题对应三种算法动手写代码前先把三个任务各自要回答的问题写出来避免做到一半逻辑乱掉任务算法输入特征要回答的问题餐厅分群KMeans价格档位、星级、城市密度米其林餐厅能被分成哪几类客群各自有什么特征菜系-城市共现Apriori餐厅菜系与城市的布尔矩阵哪些菜系和城市经常同时出现形成强关联星级预测随机森林价格、菜系、城市特征一家餐厅是否有星由哪些特征说了算这个表格可以原样放进课程设计报告里。选型理由要能讲清楚聚类是探索性的没有标签也能做适合用来描述整体结构关联规则解决的是“组合频繁出现”的问题适合小规模布尔数据随机森林能输出特征重要性解释性对课程设计答辩最重要。先把这个表格定下来后面每跑一个任务都在报告里对应一段结论系统的功能面就完整了。3.2 KMeans聚类肘部法则、轮廓系数与n_init的坑聚类最容易犯的错是直接把原始列喂给 KMeans。价格是 1-4 的小整数经纬度是几十万的绝对值两者放一起算欧氏距离坐标会彻底淹没价格所有餐厅按地理位置聚成几坨完全看不出客群差异。所以第一步是标准化。然后要用肘部法则和轮廓系数确定 k不能拍脑袋from sklearn.preprocessing import StandardScaler from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score # 只挑数值特征避免字符串列 feature_cols [price_level, star_rating, city_count, city_avg_price] X df[feature_cols].copy() X_scaled StandardScaler().fit_transform(X) # 1. 肘部法则看inertia下降的拐点 inertia_list [] for k in range(2, 11): km KMeans(n_clustersk, random_state42, n_init10) km.fit(X_scaled) inertia_list.append(km.inertia_) # 2. 轮廓系数同一个k下看分群质量 for k in range(2, 11): km KMeans(n_clustersk, random_state42, n_init10).fit(X_scaled) print(fk{k}, silhouette{silhouette_score(X_scaled, km.labels_):.3f})StandardScaler 把每个特征变成均值 0、方差 1解决量纲问题这是聚类前必须做的一步忘了它后面所有分析都白搭。n_init10 表示用 10 个不同的随机初始点各跑一遍取最优结果。KMeans 的初始点是随机选的运气不好会陷入局部最优不设这个参数的话同一份数据跑两次结果可能不一样答辩时会被老师抓细节。random_state42 是个固定种子保证你提交的代码别人复跑结果一致这个细节在课程设计里非常加印象分。肘部法则的 inertia 是样本到最近中心点距离的平方和k 每加 1inertia 都会下降但下降到某个点后变缓那个拐点就是推荐的 k。轮廓系数在 [-1,1] 之间越接近 1 说明各簇分得越开课程设计里 0.25 以上就能讲“分群是有效的”不必追求 0.8 这种理想值数据本身没那么完美。跑完聚类后一定要把每个簇的均值拉出来看比如簇 1 平均价格 2.1、全是法餐簇 2 平均价格 3.8、集中在大城市这样才能给每个簇起业务名字“平价本地客群”“高端游客客群”。报告里写“聚类结果有业务解释性”比单纯贴 inertia 值高级得多。3.3 关联规则min_support、lift与事务矩阵构造关联规则在学生作品里翻车率最高原因不外乎两个一是事务矩阵构造错了把原始表直接塞给 apriori报“数据格式不对”二是阈值乱设min_support 给 0.001跑出几万条规则每条都是废话。正确做法是把数据变成“每行一个事务”的布尔矩阵。一家餐厅就是一个事务事务的项是它所属的菜系和城市然后交给 mlxtend# 构造事务矩阵每家餐厅一行菜系做one-hot basket pd.get_dummies(df.set_index(name)[cuisine]).groupby(level0).sum() basket basket.astype(bool) # min_support: 几百条记录时0.05约等于几十家餐厅共现 frequent apriori(basket, min_support0.05, use_colnamesTrue) rules association_rules(frequent, metriclift, min_threshold1.2) # 按提升度排序看前10条 print(rules.sort_values(lift, ascendingFalse).head(10)[ [antecedents, consequents, support, confidence, lift]])get_dummies 把“菜系”列展开成若干 0/1 列groupby(level0).sum() 按餐厅名做聚合这样一家餐厅如果同时有法餐和甜品两个项它的行里这两种菜系都是 1这就是一个标准事务。astype(bool) 是 mlxtend 要求的输入格式直接用 0/1 整数也能跑但显式转 bool 能避免版本差异警告。min_support0.05 的意义是数据总共几百条支持度 0.05 表示至少十几二十家餐厅同时满足这个组合低于这条线出现的规则基本是随机共现。lift 大于 1 说明“出现 A 时出现 B 的概率高于 B 整体出现的概率”说明 A 和 B 存在正向关联阈值提到 1.5 会大幅减少规则数量课程设计里 50 条以内的规则规模最好呈现。如果你的输出动辄上千条先别调算法回头审视是不是 min_support 给太低了。关联规则的结果“巴黎→法餐”这种是常识级结论老师会觉得没什么价值所以报告里最好同时写几条非显而易见的比如“东京→寿司/拉面”的置信度高或者某个二线城市和特定菜系出现强关联这些才体现挖掘价值。筛选时加一个置信度大于 0.5 的过滤条件打印条数控制在 20 条以内。3.4 星级预测类别不平衡下的小样本分类预测任务选择随机森林而不是逻辑回归理由是它能输出特征重要性方便回答“什么特征决定了餐厅有星”。逻辑回归更适合做基线和对比。小样本下随机森林最重要的两个参数是 n_estimators 和 max_depth前者控制树的棵数后者控制每棵树的深度深度太大在小样本上几乎必然过拟合。from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report X features.values y df[has_star].values X_tr, X_te, y_tr, y_te train_test_split( X, y, test_size0.2, random_state42, stratifyy) clf RandomForestClassifier( n_estimators200, max_depth6, random_state42) clf.fit(X_tr, y_tr) print(classification_report(y_te, clf.predict(X_te))) # 特征重要性直接回答“哪些特征对星级影响最大” importance pd.Series(clf.feature_importances_, indexfeatures.columns) print(importance.sort_values(ascendingFalse).head(8))stratifyy 是关键它让训练集和测试集的“有星/无星”比例保持一致否则随机切分可能把测试集里的有星样本切得只剩一两个评估结果全看运气。n_estimators200 是安全和速度的平衡点300 棵在小数据上也快但收益很小max_depth6 限制单棵树深度防止小样本过拟合。classification_report 要重点看有星那一类的 precision 和 recall而不是整体 accuracy因为“无星”占多数模型全猜无星也有 80% 以上的 accuracy看起来漂亮实际没有用。特征重要性输出要挑 top 8 写进报告如果你发现 price_level 和 city_count 排在前面说明“高消费城市餐厅聚集程度高”和获星概率强相关这就是一条可以写进报告的挖掘结论。4. 管理系统怎么搭数据库、后端接口与前端可视化算法跑通只是前半程课程设计要交付的是“管理系统”老师打开网页能上传数据、选算法、调参数、看结果。常见实现组合是 MySQL 存数据、Flask 提供 REST 接口、Vue3 写前端页面ECharts 画图。这套技术栈在课程设计里足够常见网上示例多出了 bug 也搜得到答案。4.1 系统模块划分与数据库表设计管理系统按功能拆成三大块数据管理、挖掘任务、结果展示。数据管理负责查询和修改餐厅数据挖掘任务负责选算法、填参数、运行结果展示负责把聚类图、规则表、评估指标画出来。数据库至少要三张表把“任务记录”独立出来是为了实现历史任务回看——老师点历史任务就能看到上次跑的参数和结果而不是每次都要重跑表名关键字段用途restaurantid, name, city, cuisine, price_level, star_rating, latitude, longitude清洗后的餐厅主数据mining_taskid, algorithm, param_json, status, created_at每次挖掘任务的参数与状态mining_resultid, task_id, result_json, created_at任务输出的结果JSON 格式建表 SQL 要特别注意字符集否则中文全部变问号CREATE DATABASE michelin DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE restaurant ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(200) NOT NULL, city VARCHAR(64), cuisine VARCHAR(64), price_level TINYINT, star_rating TINYINT, latitude FLOAT, longitude FLOAT, INDEX idx_city (city), INDEX idx_cuisine (cuisine) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE mining_task ( id INT PRIMARY KEY AUTO_INCREMENT, algorithm VARCHAR(32), param_json TEXT, status VARCHAR(16), created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE mining_result ( id INT PRIMARY KEY AUTO_INCREMENT, task_id INT, result_json LONGTEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;utf8mb4 能存 emoji 和生僻字MySQL 8.0 默认就是它但如果你是 5.7 或更老版本必须在建库和建表时都显式指定连接串也要带 charsetutf8mb4三处缺一不可。param_json 和 result_json 用 TEXT/LONGTEXT 存 JSON 字符串好处是新增算法不需要改表结构坏处是无法在 SQL 层直接查询结果内容课程设计场景不存在这个需求反而省事。表的索引只用到了 city 和 cuisine 两个高频查询字段其他字段不用建索引不是越多越好。4.2 后端接口用Flask把挖掘任务封装成REST API后端的核心逻辑是从 restaurant 表读出数据转成 DataFrame然后根据请求里的 algorithm 分发到对应的挖掘函数。下面是最小可跑的 Flask 示例先把数据库连接和列表接口写出来from flask import Flask, jsonify, request from flask_sqlalchemy import SQLAlchemy app Flask(__name__) app.config[SQLALCHEMY_DATABASE_URI] ( mysqlpymysql://root:你的密码localhost/michelin?charsetutf8mb4 ) db SQLAlchemy(app) class Restaurant(db.Model): __tablename__ restaurant id db.Column(db.Integer, primary_keyTrue) name db.Column(db.String(200)) city db.Column(db.String(64)) cuisine db.Column(db.String(64)) price_level db.Column(db.Integer) star_rating db.Column(db.Integer) def to_dict(self): return {c.name: getattr(self, c.name) for c in self.__table__.columns} app.route(/api/restaurants, methods[GET]) def list_restaurants(): page request.args.get(page, 1, typeint) per_page request.args.get(per_page, 20, typeint) records Restaurant.query.paginate(pagepage, per_pageper_page) return jsonify({code: 0, data: [r.to_dict() for r in records.items]}) if __name__ __main__: app.run(debugTrue)SQLAlchemy 的模型类对应数据库表to_dict 把 SQLAlchemy 对象转成 Python 字典前端才能直接拿来渲染。paginate 返回的是分页对象用 records.items 拿当前页数据避免一次把几千条全返回。连接串里的 charsetutf8mb4 一定要写中文乱码有一半问题出在这。debugTrue 在开发时开着方便看报错上演示前要记得关掉否则报错页面会把数据库结构泄露出来。接下来是跑挖掘任务的接口。这个接口接收 algorithm 和 params从数据库读数据调用对应的算法app.route(/api/mining/run, methods[POST]) def run_mining(): data request.get_json() algorithm data.get(algorithm) params data.get(params, {}) rows Restaurant.query.all() df pd.DataFrame([r.to_dict() for r in rows]) if algorithm kmeans: from sklearn.cluster import KMeans k params.get(k, 4) feat df[[price_level, star_rating]].values km KMeans(n_clustersk, random_state42, n_init10).fit(feat) result {labels: km.labels_.tolist(), inertia: km.inertia_} elif algorithm apriori: from mlxtend.frequent_patterns import apriori, association_rules basket pd.get_dummies(df.set_index(name)[cuisine]).groupby(level0).sum().astype(bool) frequent apriori(basket, min_supportparams.get(min_support, 0.05), use_colnamesTrue) rules association_rules(frequent, metriclift, min_thresholdparams.get(min_lift, 1.2)) result {rules: rules.to_dict(records)[:50], count: len(rules)} else: return jsonify({code: 1, msg: 不支持的算法}), 400 return jsonify({code: 0, data: result})把算法逻辑放在后端而不是前端是因为浏览器不适合跑长任务而且所有算法复用同一份从数据库出来的 DataFrame代码集中、好维护。参数全部从 params 字典取前端传什么就调什么缺参时用默认值兜底前端就不会因为漏传参数而白屏。rules 打印前用 [:50] 截断防止一次返回几千条规则把浏览器卡死。这里的聚类特征只用了 price_level 和 star_rating 两个字段是演示用的最小实现真正的系统里应该从后端的 features 接口拿特征列名单而不是写死。另外本地联调时前端在 Vite 的 5173 端口、后端在 Flask 的 5000 端口存在跨域问题常见做法是给 Flask 加 flask_corsfrom flask_cors import CORS CORS(app)4.3 前端可视化用ECharts把聚类结果画成散点图前端页面不需要多花哨三个板块就够数据表格、算法参数、结果图表。图表用 ECharts散点图展示聚类规则用表格展示。核心交互是用户选聚类算法、填 k 值、点运行前端把参数 POST 到后端拿到 labels 后渲染。以 Vue3 组件为例import { ref, onMounted } from vue import * as echarts from echarts const chartRef ref(null) async function runKMeans(k) { const res await fetch(/api/mining/run, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ algorithm: kmeans, params: { k } }) }).then(r r.json()) // 把聚类标签和原始数据拼起来送给ECharts const scatterData res.data.labels.map((label, idx) ({ name: 簇${label 1}, value: [restaurants[idx].price_level, restaurants[idx].latitude], cluster: label })) const chart echarts.init(chartRef.value) chart.setOption({ xAxis: { type: value, name: 价格档位 }, yAxis: { type: value, name: 纬度 }, series: [{ type: scatter, symbolSize: 12, data: scatterData }] }) } onMounted(() runKMeans(4))div refchartRef styleheight: 400px/div这一段的核心是 res.data.labels 和 restaurants 数组的下标对齐。聚类时后端喂给 KMeans 的是 DataFrame 的行顺序前端展示时的 restaurants 也是同一个顺序只要你不中途做排序下标就是对应的。这个数据对齐问题在答辩演示时非常容易翻车比如前端按价格降序排序后画图点就对不上了。经验是图表数据永远保持后端返回的原始顺序要排序就给图表单独传一份排序后的数据不要动原始数组。ECharts 渲染前确认容器有高度上面模板里 styleheight: 400px 就为了解决这个问题。5. 避坑指南课程设计最容易翻车的5个现场做这个题目做完一轮下来踩过的坑按频率排个序下面五条几乎每组同学都会撞上至少一条。每条按现象、原因、解决展开照着排查能省掉大半天。5.1 抓官网数据被反爬拿到一堆验证码现象requests 请求返回 200可页面里全是验证码或无意义的脚手架代码再访问几次IP 直接被限制。原因米其林官网是动态渲染页面HTML 里根本没有餐厅数据数据是后端通过 JSON 接口吐给前端的同时服务器有频率限制抓太快会触发反爬。解决打开开发者工具的 Network 面板筛选 XHR 请求找一个返回餐厅列表的 JSON 接口直接请求那个接口比解析 HTML 干净得多每次请求之间加 time.sleep(1) 以上。如果接口签名复杂果断放弃自抓回去用公开数据集。课程设计不考核爬虫技术拿不到就没必要死磕。5.2 KMeans聚类结果挤成一团先查标准化现象聚类是“成功”跑完了画出来所有点堆在几个坐标附近分群边界完全看不出。原因特征量纲不一致。价格是 1 到 4经纬度是几十万的绝对值KMeans 用欧氏距离衡量相似度坐标在任何距离计算里都占据绝对主导等于没喂价格特征。解决聚类之前必须对特征做 StandardScaler 标准化这是聚类代码里必须出现的一行。以后凡是涉及距离的算法先问自己一句“特征量纲一致吗”。这是血泪教训比调 k 值的影响大一个数量级。5.3 Apriori跑不动或规则爆炸先查min_support现象apriori 一跑内存直接爆掉或者输出几千上万条规则筛不出有用的。原因事务矩阵太稀疏而 min_support 设得太低比如 0.0001任何组合都会被当成“频繁项集”apriori 的剪枝就失效了。解决先看事务矩阵规模几百家餐厅的事务min_support 从 0.05 起跳数据量增大再相应降低。另外确认事务矩阵是“每行一个餐厅”的布尔矩阵不是原始宽表。规则太多时优先提高 min_lift 而不是 min_support因为提升度过滤的才是“有没有意义的关联”支持度过滤的是“是不是太罕见”。5.4 中文乱码与入库失败先查utf8mb4现象CSV 里中文显示正常导入 MySQL 后全部变成问号或者 Flask 插入数据时报 Incorrect string value 的错误。原因数据库、表、连接串三者有一处字符集不是 utf8mb4比如 MySQL 某个老版本默认的 latin1 存不了中文。解决建库 SQL 里写死 DEFAULT CHARACTER SET utf8mb4表也写连接串加 ?charsetutf8mb4CSV 保存时选 UTF-8最好无 BOM。改完这三处还没好就是数据文件本身编码问题用文本编辑器另存为 UTF-8 再重新导入。5.5 前端图表空白先查容器高度和数据格式现象后端返回数据正常console.log 也有值ECharts 页面却一片空白。原因一种是图表容器 div 的高度为 0ECharts 的 canvas 画不进来另一种是 data 的格式和 series 类型不匹配比如散点图的 data 项需要对象数组你传了普通数组。解决先给容器写死 styleheight: 400px排除布局问题然后在浏览器控制台打印 res.data 确认字段类型最后把 setOption 拆开先画一个空轴确认图表组件本身正常再逐步往里填数据。按这个顺序排查五分钟内能定位。6. 验收答辩演示脚本与三个加分技巧6.1 三分钟演示脚本怎么排打开系统后先花 30 秒讲数据来源、抓取时间、清洗后多少条、做了哪些去重和标准化这是老师判断你基本功的第一站。然后跑一次聚类用肘部法则图说明 k 为什么取 4把每个簇的平均特征念出来比如簇 1 是高价法餐为主、簇 2 是平价本地餐厅。再展示规则表挑两条非显而易见的规则解释。最后打开历史任务列表说明系统支持任务记录。整个演示控制在三分钟重点放在“我能解释每个参数”上。6.2 答辩被追问时的三个自保动作老师最爱问的三类问题为什么用这个算法参数为什么这么定结果怎么评估对应策略算法选型问题用第 3 章开头的任务-算法表格回答参数问题就说是通过肘部法则、轮廓系数、分类报告这些客观指标定的不是拍脑袋评估问题把聚类轮廓系数、分类的 precision/recall 背出来然后补一句“在这个小样本下我更关注特征重要性而不是分数”显得你理解数据而不是只会调包。另一个加分项是源码包里的使用文档说明写清楚复现步骤包括 MySQL 建库脚本、Python 版本、依赖清单、运行顺序和每张截图对应的参数。老师拿到你的源码如果十分钟跑不通印象分会大打折扣如果照着文档一次跑通你已经赢了一半。我当时做这个题目时KMeans 的 k 参数第一次随手写了 6老师问“这个 6 是怎么来的”我只说“试出来的”被追问到尴尬。后来养成习惯每个参数都能在文档里用一句话解释依据这比代码本身更能体现你懂机器学习。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

【PMSM代码3】负载前馈与补偿的EKF,MATLAB代码。订阅专栏后可查看更多代码,都有中文注释

【PMSM代码3】负载前馈与补偿的EKF,MATLAB代码。订阅专栏后可查看更多代码,都有中文注释

如需帮助,或有滤波相关的MATLAB代码定制需求,可从个人主页左侧联系我 订阅专栏后,可直接查看源代码,粘贴到MATLAB空脚本中即可直接运行、得到结果 文章目录 总体介绍 运行结果 前馈试验电流与转矩 负载估计误差 负载前馈速度补偿 负载观测与前馈电流 命令行输出 MATLAB源代…

📅 2026/10/13 10:58:42
电力负荷预测工业级脚手架:从原始CSV到可部署模型

电力负荷预测工业级脚手架:从原始CSV到可部署模型

简介:本资源是面向人工智能与电力系统交叉领域的数据挖掘实战项目,专为计算机、自动化、电子信息等专业在校生及初阶从业者设计,完整复现大航杯‘智造扬中’电力AI大赛高分解决方案。内容涵盖从原始电力负荷与气象数据清洗、多源特征融合、节…

📅 2026/10/13 10:58:42
Drummond Group AS2认证EDI产品选型避坑指南

Drummond Group AS2认证EDI产品选型避坑指南

做 EDI 这几年,被问得最多的问题之一就是:“怎么找真正有 Drummond Group AS2 国际认证的 EDI 产品?”说实话,这个问题看起来简单,实际坑特别多。不是随便找一个支持 AS2 的软件就行,也不是销售拍着胸脯说一…

📅 2026/10/13 10:53:38
MORE NEWS

更多资讯

📰

FastAPI与Jinja2控制结构详解:条件判断、循环与宏的工程实践

1. 项目概述1.1 为什么写这个系列的第15篇写FastAPI系列写到第15篇,Jinja2模板的控制结构可以说是我最想单独拎出来讲的一块。原因很简单:很多初学者用FastAPI做Web开发时,后端接口已经写得很顺了,但一旦涉及页面渲染,…

📰

FastAPI模板进阶:掌握Jinja2控制结构实现动态页面渲染

FastAPI系列更新到第15篇,后台一直有人催更。前两篇聊了Jinja2模板的变量替换与过滤器,很多同学留言说,只知道把变量塞进HTML里远远不够——页面上有列表要循环生成,有按钮要根据状态显示或隐藏,有公共头部要复用。这部…

📰

infiAgent 完整入门指南:只改配置文件就能构建SOTA级AI智能体的无限时长Agent框架

人工智能大模型AI AgentAgent 框架多智能体AI 技能/插件 【免费下载链接】infiAgent Build your own Cowork, AI Scientist and other SoTA Agents just by editing config files. Support anthropic skills. An infinite-horizon agent framework designed for long-running, …

📰

移动端购物车实战:三层数据结构与价格计算全解析

1. 购物车这个"小功能"到底难在哪 移动端购物车,乍一看就是个列表加勾选框,很多刚入行的朋友会觉得这东西两天就能撸完。但我带过几个类似项目之后发现,真正把它做到"能用、好用、经得起折腾",工作量远超预期…

📰

分形盒维数计算实战:一维到三维的代码实现与避坑指南

简介:这套MATLAB工具资源专门面向需要掌握分形几何与盒维数计算的科研人员和工程师,提供1D、2D、3D三种空间下的分形盒维数计算实现,覆盖科赫曲线、曼德勃罗集、Sierpinski海绵等典型分形对象。包内共27个文件,以.m脚本、png结果图…

📰

Agent Skills技能化拆分:破解长提示词难题的工程实践

从去年开始,凡是认真搞过Agent应用的人,应该都体会过同一件头疼事:指令越写越长,几百行提示词堆在系统提示里,改一处就要连带改三处,模型还时不时把后面的规则忘了。后来我在不少项目里尝试给智能体做“技能…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬