PySpark实现带权重约束的多元线性回归方案咨询
问题1解答
当前Spark官方ML库(含最新3.5版本)的LinearRegression实现没有内置自定义权重约束的参数,仅支持L1、L2正则化、是否拟合截距等基础配置,无法直接满足权重和为1、非负且不超过1的约束要求。
问题2解答
你之前得到异常权重结果,大概率是UDF中特征矩阵的维度处理错误导致的:本地测试时你构造的特征矩阵维度为(特征数, 样本数),而Spark中拉取的批量数据默认按(样本数, 特征数)堆叠,未做转置的情况下损失函数计算逻辑完全错误,自然得不到正确结果。
推荐使用applyInPandas(向量化分组处理接口)替代普通UDF实现,序列化开销更低,计算效率远高于逐行调用的普通Python UDF,实现代码如下:
import pandas as pd import pyspark.sql.functions as F from pyspark.sql.types import StructType, StructField, StringType, DoubleType import numpy as np from scipy.optimize import minimize # 预定义约束和边界,特征数固定为3可直接写,动态场景可作为参数传入 cons = ({'type': 'eq', 'fun' : lambda x: np.sum(x) - 1.0}) bnds = [(0, 1) for _ in range(3)] def loss_ols(x, x_mat, y_arr): # 适配Spark拉取的(样本数, 特征数)维度矩阵,转置后做运算 return np.sum(np.square(np.dot(x, x_mat.T) - y_arr)) def train_constrained_regression(pdf): # pdf为每个分组的pandas DataFrame,包含x1/x2/x3/y字段 x_mat = pdf[['x1', 'x2', 'x3']].values y_arr = pdf['y'].values startval = np.zeros(3) res = minimize( loss_ols, startval, args=(x_mat, y_arr), method='SLSQP', constraints=cons, bounds=bnds, options={'maxiter': 100, 'disp': False} ) # 返回分组对应权重结果 return pd.DataFrame({ 'group_id': [pdf['group_id'].iloc[0]], 'b1': [round(res.x[0], 4)], 'b2': [round(res.x[1], 4)], 'b3': [round(res.x[2], 4)], 'loss': [round(res.fun, 4)] }) # 调用示例,假设原始Spark DataFrame名为df,含分组字段group_id、特征字段x1/x2/x3、标签字段y # 提前开启Arrow优化,降低序列化开销 spark.conf.set("spark.sql.execution.arrow.enabled", "true") # 定义返回结果Schema result_schema = StructType([ StructField('group_id', StringType(), True), StructField('b1', DoubleType(), True), StructField('b2', DoubleType(), True), StructField('b3', DoubleType(), True), StructField('loss', DoubleType(), True) ]) # 按分组批量求解 result_df = df.groupBy('group_id').applyInPandas( train_constrained_regression, schema=result_schema ) result_df.show()
如果是全量数据训练单模型,不需要走分布式UDF逻辑,直接把特征和y字段collect到驱动端,用本地scipy的代码计算即可,性能更好。
额外优化建议:特征数固定的场景下,可改用专门的二次规划求解器替代scipy的通用minimize接口,求解效率可提升3-5倍;提前过滤样本数小于特征数的无效分组,避免无意义的收敛计算。
内容的提问来源于stack exchange,提问作者Molly Zhou
相关产品推荐
相关产品推荐

