一、搭建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")
终于执行成功!