每年三四月份,总会有不少大四学生拿着“Hadoop+Spark新闻推荐系统”这类题目来找我调 bug。说实话,这个题目标题党很多:源码从网上能下载好几份,文档也有现成的,可是真要在本地把 HDFS、Yarn、Spark 和前端页面串起来跑通,再讲清楚“新闻标题自动分类、新闻推荐、新闻可视化”这整套逻辑,已经能淘汰一半人。很多人卡住的点根本不是算法不会,而是不懂一条数据处理链路应该如何组织,也不明白哪些部分必须用大数据组件,哪些部分其实可以用普通后端完成。
这篇内容就是把我自己做完这个题之后沉淀的拆解思路写出来。适合正在做大数据方向毕业设计的学生、准备相关岗位面试的人,以及想把“Hadoop+Spark新闻推荐系统”做成一个能演示、能答辩、能讲清楚的项目而不是只交一份 PDF 的同学。我尽量不堆术语,把整条数据流从采集、存储、清洗、分类、训练、推荐到可视化一层层剥开,顺便解释为什么这么设计、哪些环节容易翻车。
1. 就业与答辩视角下,新闻推荐系统为什么是一个合适的大数据题目
1.1 表面上是个“推荐算法题”,本质是个“平台工程题”
不少同学选这个题,是奔着“推荐算法”来的,觉得给用户推荐新闻很有趣。但仔细想想,如果你只是想实现推荐,用一台笔记本跑个 sklearn 的最近邻模型就够了,根本不需要 Hadoop。这个题目的巧妙之处在于,它把真实互联网产品的数据流程压缩成了一个可演示的系统:新闻有文本、有发布时间、有分类;用户会不断产生点击、浏览、收藏,甚至后续可以模拟出实时到达的新数据。要处理这些数据,你可以名正言顺地把 HDFS、Spark SQL、Spark MLlib、Kafka、Redis、可视化大屏都纳进来,每个组件都有自己不可替代的位置。
在面试和答辩时,这种做法得分很高。因为老师或面试官真正想看的不是你去掉第三方后端、独自从头写一个夸克推荐引擎,而是你有没有能力把一个需求拆解成多个模块,再合理分配资源:哪些用离线批处理,哪些放内存,哪些需要保存到关系型数据库供网页查询。新闻推荐系统恰好覆盖了“数据采集—数据处理—数据建模—结果展示—反馈回流”的完整闭环。哪怕每个环节实现的深度不高,链路是通的,答辩时就“有故事可讲”。
1.2 题目虽常见,为什么年年有人挂在同一个地方
同一个题目年年有人选,能拿优秀的人却很少,原因往往不是模型精度不够高,而是以下这几个问题没有处理好:
- 只在一台电脑上跑通了 Python 脚本,但没有真正经过 HDFS 存储和 Spark 任务调度,老师一问“你的 Spark 到底做了什么”,场面就非常尴尬。
- 把新闻爬下来后直接塞进了 MySQL,没有做数据清洗和文本预处理,导致后面分类模块的效果非常差,各种新闻标题都被分到“娱乐”类别里。
- 演示时靠手动点在网页上点出几张图表,点击量、分类准确率这些指标没有任何日志支撑。视频或现场答辩只能不停切换页面,说服力很弱。
- 文档里写满了 HDFS、Spark、推荐算法的概念,但实际代码仓库结构混乱,自己写完一个月后都找不到入口文件在哪,更别说让别人复现。
这四类问题本质上是同一个:只做了局部功能,没有站在系统集成视角去看项目。下面的章节,我会按照我自己推荐的“5 层架构 + 4 条主链路”来逐步还原一套可交付的 Hadoop+Spark 新闻推荐系统应该长什么样。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 新闻推荐系统的整体架构设计与技术选型逻辑
2.1 五模块分离:采集、平台、计算、服务、展示
我一般会把项目分成五个模块,而不是按“张三负责推荐、李四负责前端”这种按页面拆法。模块一旦按数据流划分,代码的边界就特别清楚:
| 模块 | 主要职责 | 常用组件 |
|---|---|---|
| 数据采集模块 | 抓取新闻标题、正文、发布时间、来源;采集用户模拟点击日志 | Scrapy / Requests、Flask 模拟日志接口 |
| 数据平台模块 | 存放原始数据与清洗后数据,形成统一的表结构 | HDFS、Hive、MySQL |
| 离线计算模块 | 每日定时跑新闻数据清洗、标题分类、ALS 用户推荐、统计报表 | Spark SQL、Spark MLlib、PySpark |
| 服务接口模块 | 给前端 Web 页面提供新闻列表、推荐列表、分类结果查询、图表数据接口 | Flask / FastAPI |
| 展示模块 | 网页端展示新闻内容、推荐反馈、可视化统计 | HTML + Vue/原生 JS + ECharts |
为什么要把 MySQL 放进去?这个问题值得细讲。HDFS 适合存储超大文件,但是不适合让 Web 后端用 SQL 去高频查询单条记录,也不适合快速响应推荐接口的 Top N 查询。所以标准做法是让 Spark 完成大规模计算后,把维度表和结果表写回 MySQL。前端打开的每个列表页、每个图表,都只和 MySQL 或 Redis 交互。这个架构在真实工业场景里很普遍,也是面试时最能体现工程经验的细节。
2.2 Spark 与 Hadoop 各自承担什么角色
很多同学把“Hadoop 和 Spark”理解成一个增强版的“大数据全家桶”,认为只要用到了这两个单词,题目就完成了。实际项目中,两者有明确分工:
- Hadoop HDFS 负责原始新闻、点击日志的分布式存储。在答辩环境中可以只搭一个伪分布式集群,但你需要说明白真实环境下为什么不能都塞进单机数据库。
- Hadoop Yarn 负责资源调度,给 Spark Application 分配内存与 CPU 资源。
- Spark 负责大规模数据清洗与特征处理。例如从 20GB 点击日志中提取每个用户的点击序列,Spark 比单机脚本快得多,代码也更流畅。
- Spark MLlib 提供的 ALS 算法可以直接训练协同过滤模型,这是推荐系统里极其经典的应用。
架构确定以后,还有一个容易忽略的“调度”问题。我建议不要在 Web 主线程里用代码触发 Spark 任务,这不是一个合格的数据平台设计。更好的方式是预留一个 Spark 任务入口脚本,用 Linux 的 crontab 或类似工具,每天凌晨以批处理方式执行分类和推荐任务。在演示时,如果你想让老师看到实时跑任务的效果,可以在后台再单独点击执行。把离线任务、在线服务分开,整个系统才不容易出现“接口被一个 Spark 任务拖死”的尴尬场景。
3. 新闻标题自动分类:短文本建模的处理经验
3.1 新闻数据库表结构与采集数据的预处理
项目的第一步是准备数据。对毕设而言,有一份结构清晰的数据表比数据量更重要。我自己习惯用这几张表:
sql复制-- 新闻分类表
create table if not exists news_category (
id int primary key,
name varchar(32)
);
-- 新闻表
create table if not exists news (
id bigint primary key auto_increment,
category_id int,
title varchar(255),
content text,
source varchar(64),
publish_time datetime
);
-- 用户点击日志表
create table if not exists user_click_log (
id bigint primary key auto_increment,
user_id bigint,
news_id bigint,
duration_seconds int,
click_time datetime
);
-- 推荐结果表,由 Spark 跑完后写入
create table if not exists user_recommendation (
user_id bigint,
news_id bigint,
score double,
reason varchar(255),
recommend_date date
);
新闻数据可以来自公开可用的新闻网站或开源的新闻数据集。如果自己做爬虫,一定要控制采集频率,只把抓取结果用于学习研究,不要对目标站点造成压力。采集完的原始内容不要直接拿来训练,首先要去除 HTML 标签、纠正标题里的特殊符号、统一时间格式、过滤明显重复的新闻。这一步我用 Pandas 与简单的 Python 脚本就能完成,因为数据量还不到海量级别,用 Spark 不划算;等真正有千万级数据时,可以把它重写成 Spark DataFrame 的 ETL 作业,逻辑几乎相同。
3.2 标题短,不是直接把文本丢给分类器那么简单
新闻标题分类最容易被忽视的地方是:标题太短。正文可能几百个字,分词之后有足够的上下文字符,但标题通常只有十几到四十个字,包含的语义信息很少。有人直接把标题丢给朴素贝叶斯,准确率能到 70% 以上,但到了财经和科技这类容易混淆的类别上就开始翻车。财经新闻标题经常出现“公司、市场、投资”,科技新闻标题也常出现“阿里、腾讯、发布会”。这时候,单靠一两个关键词判断是很不靠谱的。
我的处理方式是把标题和正文的“开篇摘要”拼接起来做训练。采集新闻时我们会保存正文内容,正文第一段的 100 到 150 个字符往往是对整个新闻的高度概括。训练分类模型时,输入特征用“标题 + 正文首段关键词”,这样既照顾了标题的简洁表达,又借用了正文的语义信息。
预处理代码如下,思路其实就是三步:去停用词、分词、转成向量。
python复制import jieba
import jieba.analyse
STOP_WORDS = set(open("stopwords.txt", encoding="utf-8").read().splitlines())
def clean_line(text: str) -> str:
# 去除多余空白,保留中文、英文和数字
text = re.sub(r"<[^>]+>", "", text)
text = re.sub(r"\s+", " ", text)
return text.strip()
def text_to_keywords(title: str, body_first_paragraph: str = "", top_k: int = 30):
full_text = title + "。" + (body_first_paragraph or "")
seg_list = jieba.cut(clean_line(full_text))
filtered = [w for w in seg_list
if w.strip() not in STOP_WORDS
and len(w.strip()) > 1
and not w.isdigit()]
# 用 TF-IDF 从拼接文本中抽关键词,而不是把所有词都留下
return " ".join(jieba.analyse.extract_tags(" ".join(filtered), topK=top_k))
这里必须注意一个坑:如果不抽关键词,而把整句话塞入向量,标题里的“涨停”“芯片”“新能源”这类词会被背景词淹没;只保留 topK 关键词以后,模型更容易识别类别独有的特征词。
3.3 在 Spark 上实现分类模型的训练与评估
新闻量比较多的场景下,建议把清洗后的文本放到 HDFS 上,然后用 PySpark 完成分类训练。我个人经常用朴素贝叶斯模型作为基线,因为它在短文本多分类上耗时低、效果稳定。下面是 PySpark 流程的骨架:
python复制from pyspark.sql import SparkSession
from pyspark.ml.feature import Tokenizer, HashingTF, IDF
from pyspark.ml.classification import NaiveBayes
from pyspark.ml import Pipeline
from pyspark.ml.evaluation import MulticlassClassificationEvaluator
from pyspark.sql.functions import length
spark = SparkSession.builder \
.appName("news_title_classifier") \
.config("spark.sql.adaptive.enabled", "true") \
.getOrCreate()
df = spark.read.parquet("hdfs:///user/bigdata/news_feature.parquet")
# 字段:label 为分类标签对应的整数,text 为 text_to_keywords 后的结果
df = df.filter(length("text") > 0)
train_df, test_df = df.randomSplit([0.8, 0.2], seed=2024)
tokenizer = Tokenizer(inputCol="text", outputCol="words")
hashing_tf = HashingTF(inputCol="words", outputCol="rawFeatures", numFeatures=10000)
idf = IDF(inputCol="rawFeatures", outputCol="features")
nb = NaiveBayes(smoothing=1.0, modelType="multinomial")
pipeline = Pipeline(stages=[tokenizer, hashing_tf, idf, nb])
model = pipeline.fit(train_df)
pred_df = model.transform(test_df)
evaluator = MulticlassClassificationEvaluator(
labelCol="label", predictionCol="prediction", metricName="accuracy"
)
acc = evaluator.evaluate(pred_df)
print("准确率: ", acc)
# 保存模型,供后续新闻入库时自动打分类标签
model.write().overwrite().save("hdfs:///models/news_title_nb_model")
有人会问,既然标题和正文首段里的关键词已经用 jieba 的 TF-IDF 提取了一次,为什么 Spark Pipeline 里还要再做 HashingTF 和 IDF?这里确实有一点重复,但实际操作中我是分两套跑的:如果数据量小,直接用 sklearn 的 TfidfVectorizer 更简单;如果数据量很大,需要演示 Spark 处理大规模数据,那就在离线 ETL 阶段只做粗清洗,到 Spark Pipeline 里再统一做词频向量化。这样不至于让训练代码里混进太多手工特征逻辑。答辩时老师如果问起来,你直接讲清楚这个尺度判断,会显得非常真实。
3.4 分类效果不好的常见原因
做完分类,最好打印一份每个类别的 Precision、Recall 和 F1。你可能会发现某个类别被严重误判。最常见的原因有三个:训练语料不均衡,比如体育类新闻是教育类的 10 倍,模型自然偏向体育;类别标签太细,例如“移动互联网”和“互联网”这类重叠标签很难分开;中文分词阶段把“大数据”“短视频”这种强区分性术语切开了。解决办法也很朴素:对样本较少的类别做重复采样,或者把容易混淆的类别适当合并成 6 到 8 个大类,对毕业设计来说,分类粒度做到“科技、财经、体育、娱乐、健康、教育”已经足够说明整套逻辑。
4. 新闻推荐系统:ALS协同过滤、内容召回与冷启动策略
4.1 点击日志怎样变成推荐模型的训练数据
新闻推荐和电商推荐有一个非常大的不同:新闻的生命周期极短,一篇新闻的热度可能持续几天,过期后几乎没人点击。所以,推荐模型要同时考虑用户兴趣和新闻时效性。在训练之前,需要把用户点击日志转换成 ALS 模型能理解的打分。
原始点击日志只有 user_id、news_id、duration_seconds、click_time。我们不能直接把点击次数作为评分,因为不同用户停留页面时长的基数差异很大。我常用的一个评分公式是:
text复制score = log2(1 + duration_seconds / 30) + news_source_weight
假如用户在新闻页停留了 60 秒,那么 log2(3) 约为 1.585。若用户快速过路只停留 3 秒,得分约 0.155。这样做的目的,是让“认真读完”的偏好体现在评分里,而不是简单地把偶然打开也算一次强反馈。把多天的点击日志汇总成一张“用户—新闻—行为评分”表后,就可以灌入 ALS 模型。
4.2 PySpark 中 ALS 模型如何训练和推荐
ALS 的全称是交替最小二乘法,是 Spark MLlib 里实现比较成熟的协同过滤算法。它的输入既可以是显式评分,也可以是隐式反馈。对于新闻点击这种自然产生的行为,我们一般把模型配置为隐式反馈模式:
python复制from pyspark.ml.recommendation import ALS
from pyspark.ml.evaluation import RegressionEvaluator
als = ALS(
maxIter=10,
rank=20,
regParam=0.1,
alpha=1.0,
implicitPrefs=True,
userCol="user_id",
itemCol="news_id",
ratingCol="score",
coldStartStrategy="drop"
)
als_model = als.fit(train_rating)
模型训练完成后,以下代码会为每一个用户计算其可能感兴趣的全部新闻,输出 TopN。有一个绝对要注意的坑:ALS 会计算出用户和全量新闻的预测分数,但新闻库里很多是几天前的旧闻。如果直接把 ALS 的原始结果拿出来当最终推荐,会被批评“没有结合新闻场景”。因此,训练结束后要对候选新闻做一次过滤,过滤掉超过保质期的内容、已下架的新闻和用户已经点击过的新闻。这个步骤在真实平台上是必要的,答辩时讲不出这一步,说明你还没弄懂推荐系统落地和跑离线实验之间的差距。
4.3 推荐融合:离线协同过滤、内容分类召回、热度兜底的组合
ALS 协同过滤有一个天生的问题:新新闻没有任何用户行为,模型无法对它产生评分,这叫冷启动。新闻系统的新闻更新又极快,所以必须给推荐接口增加几条旁路策略。
我实现的最终推荐结果没有只依赖 ALS,而是做了一个加权的多路召回。推荐分为三层:
- 候选层:把实时新入库的新闻、最近三天高热度新闻、ALS 模型预测分高的新闻合并到一个候选集合,按新闻 ID 去重。
- 排序层:对集合中的每条新闻计算融合分。
- 过滤层:过滤掉用户历史点击过的新闻、分类过偏的新闻,以及明显有重复标题的内容。
融合分公式可以简单写成:
text复制final_score =
0.55 * als_score +
0.30 * similarity_score +
0.15 * hot_score
这里的 similarity_score 指的是用户最近点击过的新闻类别的偏好分。用户如果最近点击了“科技”类新闻,那么候选集合里同时属于“科技”类的新闻会获得一个额外加分。hot_score 则通过新闻最近一小时点击量标准化计算。用这个融合公式,既保住了老用户对 ALS 推荐结果的稳定性感知,也保证了新入库新闻有机会被推送出来。前端展示时,每条推荐结果还应该带一个“猜你喜欢”“同类热门”“刚刚发布”这类推荐理由字段,实现上其实只是在结果表里多存一个 reason 字段。这个小细节对演示观感提升非常大。
5. “新闻可视化与数据分析”怎么做得让人眼前一亮
5.1 不要只做三个饼图,用图表讲清楚数据流
新闻可视化是很多人最容易敷衍掉的部分。有的项目只放两个 ECharts 饼图:一个新闻分类分布,一个点击来源分布。说实话,这种程度还不如不做。用来支撑“新闻数据分析”的可视化,应该从不同提问角度组织:
| 图表名称 | 数据计算逻辑 | 想说明的问题 |
|---|---|---|
| 新闻发布趋势折线图 | 按小时/天统计新闻条数 | 各时段新闻供给量波动 |
| 新闻分类堆叠面积图 | 按天统计各个类别的发布量 | 一周内不同频道的内容供给变化 |
| 点击 Top10 柱状图 | 关联点击日志计算新闻点击数 | 热度最高的标题有哪些共同点 |
| 新闻标题词云 | 对热点击新闻标题做 TF-IDF 提取词频 | 当前大家最关心什么话题 |
| 用户点击时段热力图 | 将点击时间映射到星期与小时 | 用户访问高峰分布情况 |
| 推荐点击率变化趋势图 | 统计推荐位点击占全部点击的比例 | 推荐系统实际产生的效果 |
这些图表如果全部用 SQL 手动统计,工作量会很大。正确做法是让 Spark SQL 夜间定时跑出统计数据,写回 MySQL 的报表表中,前端 ECharts 再从接口读。统计任务可以写得很直接,例如:
python复制from pyspark.sql import functions as F
click_df = spark.read \
.option("header", True) \
.csv("hdfs:///data/click_logs/*.csv")
report_df = click_df.groupBy("news_category", "hour") \
.agg(F.count("*").alias("click_cnt")) \
.sort("news_category", "hour")
report_df.write \
.mode("overwrite") \
.jdbc(url=jdbc_url, table="report_click_by_category_hour", properties=db_props)
5.2 数据分析要能回答“然后呢”
“可视化”只是表达形式,真正的加分项是分析。演示时不要只说“这个饼图显示科技类新闻最多”,而要能继续解释“科技类内容供给最多,但它的平均点击量是不是最高?如果供给多、点击低,可能是推荐策略偏向热门娱乐内容,也可能是标题本身不够吸引人”。这种带着业务口吻的解释,哪怕结论不一定很严谨,老师听了也知道你有一定的业务分析思维。
为了让分析更有内容,我建议在清洗阶段就为每条新闻多算两个字段:一个是导语关键词,一个是标题长度。然后可以对比“标题长度在 10 到 20 字之间的新闻平均点击量”和“标题超过 30 字的新闻平均点击量”,由图表结果反推:在演示数据集中,标题过短的新闻是不是用户更容易忽略?这种小结论不需要高深算法,却能成为论文里“数据分析”章节的支撑。
5.3 实时可视化的实现尺度和如何避免翻车
标题里有大数据、新闻推荐,很多同学就想加 Spark Streaming 或 Structured Streaming 模块,模拟实时计算。这是个好想法,但一不小心会让整个项目变成“演示翻车现场”。实时模块最稳妥的切入点是:用 Python 写一个模拟行为发送脚本,每秒钟向 Kafka 写入几条用户点击消息,再由 Spark Structured Streaming 从 Kafka 消费,聚合最近一分钟的点击量,写回流表。前端图表每 5 到 10 秒刷新一次,就能看到流量数字实时变化。
这个做法有两个核心好处:第一,架构上真正引入了实时流处理,面试时能讲清楚 batch 与 streaming 的差异;第二,演示时因为点击数据是模拟的,不会涉及真实用户隐私,对本科毕设来说可控性很强。不过要特别提醒,如果笔记本只有 8GB 内存,同时再开 HDFS、Yarn、Zookeeper、Spark 和前端服务,跑实时流任务很容易内存不够。普通演示环境其实不用勉强上实时,把“每 5 分钟调度一次的准实时批处理”作为设计目标,已经足够说明数据处理能力。我见过太多学生现场开着多个窗口,最后因为内存爆掉而卡死在启动日志前。适度工程化才是稳赢策略。
6. 源码、文档、PPT、讲解:如何把毕设交付物变成加分项
6.1 源码仓库的目录结构,决定了老师会不会打开第二遍
毕业评阅老师通常不会逐行看代码,但打开代码仓库的第一印象会直接影响评分。一个值得模仿的目录结构是这样:
text复制news_recommend_system/
├── bin/ # 一键启动脚本,如 start_demo.sh
├── conf/ # 配置文件,数据库、HDFS 路径
├── data/ # 本地样例数据
│ ├── raw_news.csv
│ └── user_click_example.csv
├── scripts/
│ ├── 01_etl_category.py # 数据清洗与分类
│ ├── 02_train_als.py # Spark 推荐训练
│ └── 03_generate_report.py
├── web/
│ ├── app.py # Flask 后端
│ ├── static/
│ └── templates/
├── docs/
│ ├── 设计说明书.docx
│ └── 答辩PPT.pptx
└── README.md
README 不要只写“系统基于 Hadoop+Spark 实现”,至少要放一张系统架构图、一段演示录屏链接、部署步骤和测试账号。老师拿到仓库后,如果能在 5 分钟之内按 README 把服务启动,你的印象分已经和普通同学拉开差距。
6.2 说明书和 PPT 不堆概念,按“三张图、两个表”组织
很多文档都有通病:前面 30 页在抄 Hadoop 是什么、Spark 是什么,答辩时老师只能翻到最后一章提问。其实重点内容应当是“问题定义—总体设计—功能实现—实验分析”这四个方面。每章都应配具体图表,比如:
- 第一张图:项目物理架构图,体现数据如何从采集端流到展示端。
- 第二张图:模块功能图,把采集、清洗、分类、推荐、可视化五个模块画清楚。
- 第三张图:推荐时序图,描述用户点击后,系统返回推荐结果的过程。
- 两个表:新闻表和用户点击日志表的字段说明;推荐模型训练集和测试集的准确率对比表。
PPT 可以控制在 12 到 14 页,每页只解决一个问题。比页数更关键的是讲解顺序:先让老师理解你要解决什么问题,再展示你在流程中的哪个环节用了什么技术,最后用一个在线演示证明整个闭环成立。画 PPT 时我习惯用“背景数据—架构设计—核心流程—模块截图—效果评估—困难总结”的逻辑线,少放代码,多放真实运行结果截图。
6.3 讲解视频和现场答疑怎么准备
现在不少院校都要求提交讲解视频或者答辩现场演示。视频不必太长,12 到 15 分钟效果最好。一般前 2 分钟讲背景,3 到 5 分钟讲架构,6 到 9 分钟现场点开系统演示,10 到 12 分钟讲数据和模型效果,最后留 2 分钟讲项目难点与个人反思。
现场演示最容易踩的坑是“准备不彻底”:训练好的推荐模型放在本地,前端页面却连的是远程数据库;测试账号密码忘了;演示环境的端口被占用。我的习惯是提前列一份 checklist,每次排练前逐项确认:HDFS 正常、Spark 服务正常、MySQL 中报表表和推荐表已有数据、前端页面能打开、至少预留一份“一旦演示中断还能用截图救场”的备用材料。
7. 环境部署中的关键节点与我的个人实操体会
7.1 本地部署推荐的内存与版本配置
如果是在自己的笔记本上跑这个项目,我强烈建议搭建顺序从简到繁:先装好 HDFS 和 Yarn,再把 Spark 配置好,最后才写代码。网上很多教程默认你用的是 Linux 服务器,但现实是不少学生用 Windows。Windows 本地搭建 Hadoop 会引入各种 winutils 相关的兼容问题,因此最实用的办法是装虚拟机或 WSL 环境,给整套系统分配稳定资源。Hadoop 3.x、Spark 3.x、MySQL 8.x 这个组合目前资料最多,遇到问题相对容易搜到解决方案。
内存如果只有 8GB,可以把 Yarn 和 Spark 内存参数调小一点,不要全部保持默认。例如在 spark-env.sh 中设置:
bash复制export SPARK_DRIVER_MEMORY=2g
export SPARK_EXECUTOR_MEMORY=2g
同时,HDFS 存数据时不要把所有本地文件都塞进去,建议在数据目录里只保留实验用的样例数据,降低 NameNode 负担。伪分布式环境下系统能正常演示即可,没必要追求“成百上千个数据块同时写入”的性能指标。
7.2 最容易延迟到答辩前一周才发现的问题
我见过不少项目最后败在“没有预留数据回流通道”。默认链路是:新闻经过分类和推荐后被写入 MySQL,前端展示推荐列表。但用户如果在前端点击了新闻,这个行为会不会再次进入日志,并影响第二天的推荐结果?如果系统关闭了回流链路,那么你演示的推荐结果永远是静态的,推荐系统的“系统”属性就没有真正闭环。
解决回流链路的简单方案是:在 Web 后端点击新闻的接口里,同步写一条 click_log 到本地的日志文件或 Kafka 的原始日志 topic。离线任务第二天扫描前一天的新日志文件,更新 ALS 模型报表。这样整个系统从数据采集、模型训练到结果返回就形成闭环,答辩时你也可以很清楚地回答“推荐效果如何反馈,模型如何更新迭代”这类高频问题。
7.3 这是我认为整个项目中价值最高的经验
上面讲了这么多模块,最后我只想强调一句话:做好任何毕设项目,真正拉开差距的不是某一个推荐算法的调参,而是你有没有把整条数据流跑通,让每个模块互相之间有真实的数据交接。新闻推荐系统这个题目尤其如此,它天然适合做成完整的演示系统。很多同学喜欢一开始就去调朴素贝叶斯的平滑系数,或者纠结 ALS 的 rank 选 20 还是 30,结果最后连前端页面的数据接口都没有连上。我自己的经验是先把最简单的版本跑通:数据进 HDFS,Spark 能读出来,统计结果写回 MySQL,前端能显示。之后再一步步替换里面每一个模块,替换哪个,就把它的模型效果、日志和页面一起完善。
这也是为什么我建议你拿到同类项目源码时,不要先急着看神奇的推荐算法,而应该先找到它的启动脚本,理清数据从哪里来、中间经过哪些步骤、最后存在哪里。如果这个问题能在半小时之内回答清楚,这个项目基本已经成功了七成。
