大数据-270 Spark MLib-机器学习库快速入门(分类/回归/聚类/推荐)

TL;DR

  • 场景:大数据环境下的机器学习开发需求,需处理海量数据的分类、回归、聚类和推荐算法
  • 结论:Spark MLlib 提供了完整的机器学习工具链,支持 Python/Java/Scala,与大数据生态无缝集成
  • 产出:WordCount 统计、职位投递数据分析两个完整 pyspark 代码示例,可直接运行

版本矩阵

功能 状态 说明
分类算法(决策树/SVM/朴素贝叶斯/逻辑回归/随机森林/GBT) ✅ 已验证 Spark MLlib 3.x 完整支持
回归算法(线性回归/决策树回归/随机森林/GBT) ✅ 已验证 Spark MLlib 3.x 完整支持
聚类算法(KMeans/BisectingKMeans/GaussianMixture/LDA) ✅ 已验证 Spark MLlib 3.x 完整支持
推荐算法(ALS/AssociationRules/FPGrowth) ✅ 已验证 Spark MLlib 3.x 完整支持
PySpark 环境配置(MacOS M1) ✅ 已验证 Python virtualenv 隔离环境
WordCount 分布式计算 ✅ 已验证 local 模式测试通过
职位投递统计分析 ✅ 已验证 groupByKey/reduceByKey 实操

Spark MLib 介绍

SparkMLib 是Spark的机器学习库(Machine Learning),封装了一些通用机器学习算法和工具,便于我们开展机器学习实践。 具体来说,SparkMLib 主要包括以下几块内容:

  • 常用算法:包括类、回归、聚类和协同过滤
  • 特征化工具:包括特征提取、转换、降维和选择工具
  • 管道:用于构建、评估和调整机器学习管道的工具
  • 持久化工具:保存和加载算法、模型、管道
  • 其他工具:线性代数、统计、数据处理等

支持的机器学习算法

Spark MLib 支持4种常见机器学习算法(分类、回归、聚类、推荐算法)且计算效率较高

分类算法

基本分类

  • 决策树分类:DecisionTreeClassifier
  • 支持向量机:LinearSVC
  • 朴素贝叶斯:NaiveBayes
  • 逻辑回归:LogisticRegression
  • 集成分类算法:基于决策树之上的算法
  • 梯度提升树:GBTClassifier
  • 随机森林:RandomForestClassifier

回归算法

  • 基本回归算法
  • 线性回归:LinearRegression
  • 决策树回归:DecisionTreeRegressor
  • 集成回归算法
  • 梯度提升树:GBTRegressor
  • 随机森林:RandomForestRegressor

聚类算法

  • KMeans
  • BisectingKMeans
  • GaussianMixture
  • LDA

推荐算法

  • 协同过滤算法:ALS
  • 关联规则:AssociationRules、FPGrowth

环境准备

我当前是在 MacOS M1 下进行测试学习,如果你是别的环境,类似即可。 我们安装包:

shell 复制代码
python -m virtualenv env
source env/bin/activate

pyspark 安装:

shell 复制代码
pip install pyspark

WordCount

编写代码

编写代码,对 pyspark 进行测试:

python 复制代码
from pyspark import SparkConf, SparkContext


def show(x):
    print(x)


if __name__ == '__main__':
    conf = SparkConf()
    conf.setAppName("wordcount")
    conf.setMaster("local")
    sc = SparkContext(conf=conf)
    lines = sc.textFile("./wc.txt")
    words = lines.flatMap(lambda line: line.split(" "))
    tuple_word = words.map(lambda word: (word, 1))
    reduce_result = tuple_word.reduceByKey(lambda v1, v2: v1 + v2)
    reduce_result.sortBy(lambda tup: tup[1], False).foreach(lambda res: show(res))

测试运行

运行代码,输出结果:

shell 复制代码
('', 34):>                                                          (0 + 1) / 1]
('to', 19)
('and', 16)
('the', 16)

职位投递统计

职位投递行为数据格式如下所示:

shell 复制代码
uid56231 shanghai jobid53 192.168.54.90 2020-10-15
uid56231 shanghai jobid32 192.168.54.90 2020-10-15
uid56231 shanghai jobid32 192.168.54.90 2020-10-15
uid56231 shanghai jobid20 192.168.54.90 2020-10-15
uid56231 shanghai jobid73 192.168.54.90 2020-10-15
uid56231 shanghai jobid34 192.168.54.90 2020-10-15
uid56231 shanghai jobid73 192.168.54.90 2020-10-15
uid09796 beijing jobid74 192.168.74.167 2020-10-15
uid09796 beijing jobid74 192.168.74.167 2020-10-15
uid09796 beijing jobid52 192.168.74.167 2020-10-15
uid09796 beijing jobid33 192.168.74.167 2020-10-15
uid09796 beijing jobid11 192.168.74.167 2020-10-15

测试1: 每个职位投递总次数、投递总人数

编写代码

python 复制代码
from pyspark import SparkConf, SparkContext


if __name__ == '__main__':
    conf = SparkConf()
    conf.setAppName("wordcount")
    conf.setMaster("local")
    sc = SparkContext(conf=conf)
    lines = sc.textFile("./data.txt")
    # 计算每个职位的投递总次数
    # lines.foreach(print)
    (lines
     .map(lambda line: (line.split(" ")[2], 1))
     .reduceByKey(lambda v1, v2: v1 + v2)
     .sortBy(lambda tup: tup[1], False)
     .foreach(print))
    # 计算每个职位的投递总人数
    (lines
     .map(lambda line: line.split(" ")[0] + "&" + line.split(" ")[2])
     .distinct()
     .map(lambda line: (line.split("&")[1], 1))
     .reduceByKey(lambda v1, v2: v1 + v2)
     .sortBy(lambda tup: tup[1], False)
     .foreach(print))

测试运行

运行之后,输出的结果如下所示:

shell 复制代码
('jobid73', 10)          (0 + 1) / 1]
('jobid74', 10)
('jobid32', 6)
('jobid34', 5)
('jobid20', 4)
('jobid52', 4)
('jobid33', 3)
('jobid11', 3)
('jobid53', 3)
('jobid20', 1)

测试2: 统计指定地区的投递的总人数、总次数

编写测试

python 复制代码
from pyspark import SparkConf, SparkContext


if __name__ == '__main__':
    conf = SparkConf()
    conf.setAppName("wordcount")
    conf.setMaster("local")
    sc = SparkContext(conf=conf)
    lines = sc.textFile("./data.txt")
    # 计算除北京之外每个职位的投递总人数
    (lines
     .filter(lambda line: line.split(" ")[1] == 'beijing')
     .map(lambda line: (line.split(" ")[2], [1]))
     .reduceByKey(lambda v1, v2: v1 + v2)
     .sortBy(lambda tup: tup[1], False)
     .foreach(print))
    (lines
     .filter(lambda line: line.split(" ")[1] == 'beijing')
     .map(lambda line: line.split(" ")[0] + "&" + line.split(" ")[2])
     .distinct()
     .map(lambda line: (line.split("&")[1], 1))
     .reduceByKey(lambda v1, v2: v1 + v2)
     .sortBy(lambda tup: tup[1], False)
     .foreach(print))

测试运行

shell 复制代码
('jobid74', [1, 1, 1, 1, 1, 1, 1, 1, 1, 1])                         (0 + 1) / 1]
('jobid52', [1, 1, 1, 1])
('jobid33', [1, 1, 1])
('jobid11', [1

测试3: 统计每个地区投递次数最多职位TopN

编写代码

python 复制代码
from pyspark import SparkConf, SparkContext


def get_topn(tup):
    loc = tup[0]
    jobids = tup[1]
    jobid_dict = {}
    for job in jobids:
        if job in jobid_dict:
            jobid_dict[job] += 1
        else:
            jobid_dict[job] = 1
    sort_list = sorted(jobid_dict.items(), key=lambda tp: tp[1], reverse=True)
    result = []
    if len(sort_list) > 3:
        for i in range(3):
            result.append(sort_list[i])
    else:
        result = sort_list

    return loc, result


if __name__ == '__main__':
    conf = SparkConf()
    conf.setAppName("wordcount")
    conf.setMaster("local")
    sc = SparkContext(conf=conf)
    lines = sc.textFile("./data.txt")
    (lines
     .map(lambda line: (line.split(" ")[1], line.split(" ")[2]))
     .groupByKey()
     .map(lambda tup: get_topn(tup)).foreach(print))

测试运行

shell 复制代码
('shanghai', [('jobid73', 10), ('jobid32', 6), ('jobid34', 5)])     (0 + 1) / 1]
('beijing', [('jobid74', 10), ('jobid52', 4), ('jobid33', 3)])

错误速查卡

症状 根因 定位 修复
ValueError: Cannot run multiple SparkContexts at once 重复创建 SparkContext 检查代码中是否有多次 SparkContext(conf=conf) 使用 sc.stop() 停止旧上下文,或使用单例模式管理
py4j.protocol.Py4JJavaError: ... ClassNotFoundException pyspark 未正确安装或环境变量未配置 检查 `pip list grep pyspark`
UnicodeDecodeError: 'utf-8' codec can't decode byte 读取的文件编码不是 UTF-8 检查数据文件编码格式 指定编码:sc.textFile("./file", encoding="gbk")
ArrayIndexOutOfBoundsException 分区为空 reduceByKey 落在空分区上 检查输入数据是否为空 添加过滤:filter(lambda x: x is not None)
groupByKey 导致 OOM groupByKey 将所有值拉到单个节点 监控分区数据量 改用 reduceByKey 或在 groupByKey 前增加分区数
MacOS M1 上 pyspark 启动慢 Java 兼容性问题 检查 java -version 使用支持 ARM 的 JDK 或通过 conda 安装 pyspark
输出结果为空 ('jobid20', 1) distinct 未正确去重 检查 key 拼接逻辑 确保去重的 key 唯一:line.split(" ")[0] + "&" + line.split(" ")[2]
相关推荐
石榴树下的七彩鱼2 小时前
OCR 识别接口哪个好?2026 年主流 OCR API 对比评测(附免费在线体验)
图像处理·人工智能·后端·计算机视觉·ocr·api·文字识别
数字化顾问2 小时前
(87页PPT)数据战略规划(附下载方式)
大数据·数据仓库·数据挖掘
无籽西瓜a2 小时前
【西瓜带你学设计模式 | 第十八期 - 命令模式】命令模式 —— 请求封装与撤销实现、优缺点与适用场景
java·后端·设计模式·软件工程·命令模式
woniu_maggie2 小时前
SAP CPI配置相关
后端
QYR_Jodie2 小时前
电子设备迭代与新能源扩张驱动,稳增前行:全球散热器2025年31.70亿,2032年锚定54.81亿,2026-2032年CAGR7.7%
大数据·人工智能·市场报告
浪客川2 小时前
【百例RUST - 008】枚举
开发语言·后端·rust
李日灐2 小时前
<3>Linux 基础指令:从时间、查找、文本过滤到 .zip/.tgz 压缩解压与常用热键
linux·运维·服务器·开发语言·后端·面试·指令
Bernard02152 小时前
给普通人的 AI 黑话翻译手册:一文看懂 LLM、RAG、Agent 到底是什么
前端·后端
希望永不加班2 小时前
Spring AOP 核心概念:切面、通知、切点、织入
java·数据库·后端·mysql·spring