pyspark 机器学习 实例1

  • pyspark实例1,使用随机森林(回归)预测保险成本

1.首先,引入必要的包

from pyspark.sql import SparkSession
spark = # 根据自己的机器定
sc = spark.sparkContext

from pyspark.ml.feature import StringIndexer, VectorAssembler
from pyspark.ml.regression import RandomForestRegressor, RandomForestRegressionModel
from pyspark.ml import Pipeline, PipelineModel
from pyspark.ml.evaluation import RegressionEvaluator
from pyspark.ml.tuning import ParamGridBuilder, CrossValidator
from pyspark.ml.evaluation import RegressionEvaluator
import pandas as pd

2. 读取数据

# 这里使用文本文件作为输入源
trainInput = (spark.read
  .option("header", "true")      # 指定有header
  .option("inferSchema", "true") # 自动推断数据类型
  .csv('文件绝对路径')
  .cache()) # 使用cache()将数据存储在memory中而不是分开读取,提高调用速度
testInput = (spark.read
  .option("header", "true")      # 指定有header
  .option("inferSchema", "true") # 自动推断数据类型
  .csv('文件绝对路径')
  .cache()) 

train文件下载地址https://pan.baidu.com/s/1qDFeaNnoXfQwbChKL3dJ1A 提取码i3pp
test文件下载地址https://pan.baidu.com/s/1gZ9ZFmk4HCb-ZVsoVqEKHw 提取码wmat

分割数据集

# 通常来说,会将训练数据集分成两份, 一份作为训练集,一份作为验证集
data = trainInput.withColumnRenamed('loss', 'label') # 重命名列, loss列作为类标签
# 分割训练集验证集
trainingData, validationData = data.randomSplit([0.8,0.2]) # 随机划分训练集
trainingData.cache() # 持久化
validationData.cache()

数据预处理step1

# 原始数据有多列,其中'cat'开头的列为标称列(category 非数字)
# 将category类数据进行转化, StringIndexer或者是OneHotEncoder都可以, 这里使用StringIndexer
iscate = lambda x: x.startswith("cat") # 如果string开头是'cat'则返回True
cateNew = lambda x:f'new_{x}' if iscate(x) else x # 如果是'cat' 开头返回'new_cat...',否则返回原名称
# 这里产生一个StringIndexer模型的列表,用于存储每一个'cat'列的StringIndexer模型,StringIndexer模型会把string转成数字
# eg: ['A','B','C','A'] ==> [1.0,2.0,3.0,1.0]
stringIndexerStages = list(map(lambda x:
                               StringIndexer(inputCol=x, outputCol=cateNew(x))
                               .fit(trainInput.select(x).union(testInput.select(x)))
                               ,filter(iscate, trainInput.columns))) # 如果是'cat'列,则产生一个StringIndexer
# 这里StringIndexer(inputCol=c, outputCol=categNewCol(c)).fit 
# 相当于 model = StringIndexer(inputCol=c, outputCol=categNewCol(c)); model.fit

数据预处理step2

from pyspark.sql.functions import col, countDistinct
# 这里*表示将*后括号中的操作最为一组独立操作,作为一列返回,不加*会报错
# col('列名')换成trainingData['列名']都可以
unique_count = trainingData.agg(*(countDistinct(col(c)).alias(c) for c in trainingData.columns if 'cat' in c))# 发明回一个单列的dataframe
unique_count = unique_count.toPandas().to_dict('records')[0] # 转成字典
# 查看cat每列元素unique个数
unique_count = list(sorted(unique_count.items(),key = lambda x:x[1],reverse = True))
unique_count
[out]:
[('cat116', 308),
 ('cat110', 129),
 ('cat109', 81),
 ('cat113', 61),
 ('cat112', 51),
 ('cat115', 22),
...]
可以看出cat115最前的列数量都很大,很可能是ID或者序号之类的数据,没有多大意义
import re
onlyFeatureCols = lambda c: not re.match(r"id|label", c)
featureCols = map(cateNew, filter(onlyFeatureCols, 
                                      map(lambda x:x[0], filter(lambda x:x[1]<=22, unique_count))))
# 将剩下的cont列(数字列)添加都featureCols列表中
featureCols = list(featureCols)
featureCols.extend(filter(lambda x:'cont' in x, trainingData.columns))

模型组装

# 列变为向量
assembler = VectorAssembler(inputCols=featureCols, outputCol="features") 
#使用随机森林进行回归
rf = RandomForestRegressor(featuresCol="features", labelCol="label")
stages = stringIndexerStages # stringIndexerStages是stringIndexerStage模型的列表
stages.append(assembler) # 将向量化加入stages中
stages.append(rf) #将随机森林模型加入stages中
#构建pipeline
pipeline = Pipeline(stages=stages)
# pipeline工作流程如下:
# 首先,pipeline会依次使用stringIndexerStages产生多个新的new_cat...列(字符转数字)
# 其次,  pipeline会使用assembler将featureCols列表中所有的列转化为列名为'features'的# 向量
# 最后, pipeline会使用RandomForestRegressor训练模型

超参数调节

numTrees = [5, 20] # 随机森林回归树个数
maxDepth = [4, 6] # 每棵树最大深度
maxBins = [32] # 提到预测精度的参数
numFolds = 3 # 3折教程验证
# 产生 一个gridsearch 类似于sklearn
paramGrid = (ParamGridBuilder()
  .addGrid(algo.numTrees, numTrees)
  .addGrid(algo.maxDepth, maxDepth)
  .addGrid(algo.maxBins, maxBins)
  .build())

交叉验证

cv = CrossValidator(estimator=pipeline, # 模型为pipeline管道
                    evaluator=RegressionEvaluator(), # 评估函数为回归评估函数
                    estimatorParamMaps=paramGrid, # 交叉验证参数列表
                    numFolds=numFolds) # 每组参数k折验证次数
cvModel = cv.fit(trainingData) # 训练模型

结果评估

# 预测评估训练集和验证集结果
trainPredictionsAndLabels = cvModel.transform(trainingData).select("label", "prediction")
validPredictionsAndLabels = cvModel.transform(validationData).select("label", "prediction")
evaluator = RegressionEvaluator(predictionCol="prediction")
evaluator.evaluate(trainPredictionsAndLabels)

trainRegressionMetrics = evaluator.evaluate(trainPredictionsAndLabels, {evaluator.metricName: "rmse"})# 使用rmse评估
validRegressionMetrics = evaluator.evaluate(validPredictionsAndLabels, {evaluator.metricName: "rmse"})# 使用rmse评估

print(f'训练集rmse结果:{trainRegressionMetrics}')
print(f'验证集rmse结果:{validRegressionMetrics}')
bestModel = cvModel.bestModel # 取出最好的模型
# stages[-1]表示 模型组装 那一步最后加的RandomForestRegressor模型
featureImportances = bestModel.stages[-1].featureImportances.toArray()
print('属性重要度')
print(list(zip(featureCols, featureImportances)))
[out]:
 训练集rmse结果:2062.983881979985
 验证集rmse结果:2224.3549229894074
属性重要度:
[('idx_cat115', 0.0033743166898874697),
 ('idx_cat105', 0.01018581426322841),
 ('idx_cat107', 0.0014754211661747892),
 ('idx_cat114', 0.003115644988119102),
 ('idx_cat101', 0.04351954608508133),
 ('idx_cat104', 0.0014860643707163682),
 ('idx_cat106', 0.014912628744990015),
 ('idx_cat99', 0.0007673281733985146),
...]

你可能感兴趣的:(pyspark 机器学习 实例1)