使用spark的ALS算法基于grouplens数据集的推荐实例

一、搭建spark环境

spark环境基于bitnami/spark镜像搭建,运行了一个mast一个work。使用docker-compose命令配置并启动。没有hadoop,基于本地文件系统存储,需要绑定volumes。

#建立网络
docker network create spark_default
#创建容器
docker-compose -f docker-compose.yml create
#启动
docker-compose up -d

 docker-compose.yml文件如下。

version: '2'

services:
  spark:
    image: bitnami/spark:3.2.1
    environment:
      - SPARK_MODE=master
      - SPARK_RPC_AUTHENTICATION_ENABLED=no
      - SPARK_RPC_ENCRYPTION_ENABLED=no
      - SPARK_LOCAL_STORAGE_ENCRYPTION_ENABLED=no
      - SPARK_SSL_ENABLED=no
    ports:
      - '8080:8080'
    volumes:
      - 'G:/docker-share/tmp:/tmp'
  spark-worker:
    image: bitnami/spark:3.2.1
    environment:
      - SPARK_MODE=worker
      - SPARK_MASTER_URL=spark://spark:7077
      - SPARK_WORKER_MEMORY=1G
      - SPARK_WORKER_CORES=1
      - SPARK_RPC_AUTHENTICATION_ENABLED=no
      - SPARK_RPC_ENCRYPTION_ENABLED=no
      - SPARK_LOCAL_STORAGE_ENCRYPTION_ENABLED=no
      - SPARK_SSL_ENABLED=no
    volumes:
      - 'G:/docker-share/tmp:/tmp'

二、执行推荐程序

spark example提供了基于ALS的推荐程序。通过观察到的所有用户给产品的打分,来推断每个用户的喜好并向用户推荐适合的产品。

ALS是交替最小二乘(alternating least squares)的简称,是基于矩形分解的一种方法。算法参见ALS算法

java使用代码如

MatrixFactorizationModel model = ALS.train(ratings, rank, iterations,lambda)

参数如下:

1、ratings:训练集,数据格式:(用户id 物品id 评分 )
2、rank:矩阵分解时对应的低维的维数,即特征向量维数或者说特征数。如果这个值太小拟合的就会不够,误差就很大;如果这个值很大,就会导致模型大泛化能力较差。这个值会影响矩阵分解的性能,越大则算法运行的时间和占用的内存可能会越多。通常需要进行调参,一般可以取10-200之间的数。
3、iterations:在矩阵分解用交替最小二乘法求解时,进行迭代的最大次数。一般来说,不需要太大,比如5-20次即可。
4、lambda:正则因子。lambda也是和rank一样的,可以防止过拟合问题,如果设置为0,那么就不会有防止过拟合的功能;可以从[0.0001 ,10]范围,按倍数选值测试结果。

遇到的问题:

1、出现java.lang.StackOverflowError异常,搜索资料设置属性spark.executor.extraJavaOptions  -Xss30M,依然执行失败。

2、关键参数是--kryo,序列化的方式,没想到是它。只要有这个参数几乎都能成功运行。

bin/run-example org.apache.spark.examples.mllib.MovieLensALS  --rank 10 --kryo ./data/mllib/sample_movielens_data.txt

三、执行python的例子

这个例子是根据grouplens数据的100K数据集整理的。

完整例子参见pyspark ALS例子

#!/opt/bitnami/spark/venv/bin/python3
# coding=utf-8

import sys
sys.path.append( '/opt/bitnami/spark/venv/lib/python3.8/site-packages/' )

from pyspark import SparkContext
from pyspark import SparkConf
from pyspark.mllib.recommendation import ALS



def CreateSparkContext():
    sparkConf = SparkConf() \
                .setAppName("RecommendTrain") \
                .set("spark.ui.showConsoleProgress", "false") \

    sc = SparkContext(conf = sparkConf)
    sc.setSystemProperty("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
    print("master="+sc.master)
    SetLogger(sc)
    SetPath(sc)
    return sc

def SetLogger(sc):
    logger = sc._jvm.org.apache.log4j
    logger.LogManager.getLogger("org").setLevel(logger.Level.ERROR)
    logger.LogManager.getLogger("akka").setLevel(logger.Level.ERROR)
    logger.LogManager.getRootLogger().setLevel(logger.Level.ERROR)

def SetPath(sc):
    global Path
    if sc.master[0:5] == "local":
        Path = "file:/tmp/data/ml-100k/"
    else:
        Path = "hdfs://master:9000/user/hduser/"

def SaveModel(sc):
    try:
        model.save(sc, Path+"movie/ALSmodel")
        print("Model is saved")
    except:
        print("Error,Model is already exist")


def PrepareData(sc):
    file = sc.textFile(Path + "u.data")
    rawRatings = file.map(lambda line: line.split("\t")[:3])
    ratingsRDD = rawRatings.map(lambda x: (x[0], x[1], x[2]))
    return ratingsRDD

if __name__ == "__main__":
    sc = CreateSparkContext()
    print("-----------Preparing----------")
    ratingsRDD = PrepareData(sc)
    print("-----------Training-----------")
    print("Start ALS training, rank=5,iterations=20, lambda=0.1")
    model = ALS.train(ratingsRDD, 5, 20, 0.1)
    print("-----------Saving Model-----------")
    SaveModel(sc)

执行时遇到的问题:

1、启动时找不到numpy模块。根据经验添加numpy模块的路径,

import sys
sys.path.append( '/opt/bitnami/spark/venv/lib/python3.8/site-packages/' )

2、经过上一步可以运行了,但运行期还是报找不到numpy模块的错误。通过手工调试,定位是python环境的问题。镜像采用venv虚拟环境的方式安装python,所以有2个python执行程序。因此在环境变量PATH中添加python虚拟环境的路径export PATH=/opt/bitnami/spark/venv/bin/:$PATH。

3、程序又可以运行了,但是同样报java.lang.StackOverflowError异常。因为pyspark采用py4j的方式调用java的实现,所以报java的异常,估计同样是序列化的问题。

设置序列化参数 sc.setSystemProperty("spark.serializer", "org.apache.spark.serializer.KryoSerializer")

终于执行成功!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值