You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Pulp求解器时Jupyter Notebook内核持续崩溃问题求助

问题描述

运行Jupyter Notebook中的LP(线性规划)求解器脚本时,执行最后一行代码出现错误:The kernel appears to have died. It will restart automatically.,在普通Python脚本中运行也会出现相同问题。使用的数据集dfs_proj为240行、5列。

代码如下:

import pandas as pd
from pulp import *
from pulp import LpMaximize

dfs_proj = pd.read_csv("4for4_dfs_projections_120321.csv")
dfs_proj['count'] = 1
cols = ['Player', 'Pos', 'FFPts', 'DK ($)', 'count']
dfs_proj = dfs_proj[cols]
dfs_proj = dfs_proj[(dfs_proj['DK ($)'] >= 4000) | (dfs_proj['Pos'] == "DEF") | (dfs_proj['Pos'] == "TE")]

player_dict = dict(zip(dfs_proj['Player'], dfs_proj['count']))

# create a helper function to return the number of players assigned each position
def get_position_sum(player_vars, df, position):
    return pulp.lpSum([player_vars[i] * (position in df['Pos'].iloc[i]) for i in range(len(df))])

def get_optimals(site, data, num_lineups, optimize_on='FFPts'):
    """
    Generates x number of optimal lineups, based on the column to
    designate as the one to optimize on.
    :param str site: DK or FD. Used for salary constraints
    :param pd.DataFrame data: Pandas dataframe containing projections.
    :param int num_lineups: Number of lineups to generate.
    :param str optimize_on: Name of column in dataframe to use when optimizing
    """
    #global lineups
    lineups = []
    player_dict = dict(zip(data['Player'], data['count']))
    for i in range(1, num_lineups+1):
        prob = pulp.LpProblem('DK_NFL_weekly', pulp.const.LpMaximize)
        player_vars = []
        for row in data.itertuples():
            var = pulp.LpVariable(f'{row.Player}', cat='Binary')
            player_vars.append((row.Player, var))
        # total assigned players constraint
        prob += pulp.lpSum(player_var for player_var in player_vars) == 9
        # total salary constraint
        prob += pulp.lpSum(data['DK ($)'].iloc[i] * player_vars[i][1] for i in range(len(data))) <= 50000
        # for QB and DST, require 1 of each in the lineup
        prob += get_position_sum(player_vars, df, 'QB') == 1
        prob += get_position_sum(player_vars, df, 'DEF') == 1
        
        # to account for the FLEX position, we allow additional selections of the 3 FLEX-eligible positions: RB, WR, TE
        prob += get_position_sum(player_vars, df, 'RB') >= 2
        prob += get_position_sum(player_vars, df, 'WR') >= 3
        prob += get_position_sum(player_vars, df, 'TE') >= 1
        if i > 1:
            if optimize_on == 'Optimal Frequency':
                prob += pulp.lpSum([data['FFPts'].iloc[i] * player_vars[i][1] for i in range(len(data))]) <= (optimal - 0.001)
            else:
                prob += pulp.lpSum([data['FFPts'].iloc[i] * player_vars[i][1] for i in range(len(data))]) <= (optimal - 0.01)
        
        prob += pulp.lpSum([data['FFPts'].iloc[i] * player_vars[i][1] for i in range(len(data))])
        # solve and print the status
        prob.solve(PULP_CBC_CMD(msg=False))
        optimal = prob.objective.value()
        count = 1
        lineup = {}
        for i in range(len(data)):    
            if player_vars[i][1].value() == 1:
                row = data.iloc[i]
                lineup[f'G{count}'] = row['Player']
                count += 1
            lineup['Total Points'] = optimal
        
        lineups.append(lineup)
        players = list(lineup.values())
        for i in range(0, len(players)):
            if type(players[i]) == str:
                player_dict[players[i]] += 1
                if player_dict[players[i]] == 45:
                    data = data[data['Player'] != players[i]]
    return lineups

lineups = get_optimals(dfs_proj, 20, 'FFPts')
问题分析

内核崩溃的核心原因是代码存在逻辑错误,同时内存使用效率低下:

  • 参数顺序不匹配:调用get_optimals时参数顺序与函数定义不符,导致site参数被传入数据集,后续处理类型错误。
  • 未定义变量:get_position_sum调用中使用了未定义的df变量,应该使用函数传入的data参数。
  • 循环变量冲突:外层循环的i与内层遍历数据的i重名,导致薪资约束计算错误,进而引发求解器异常。
  • 内存浪费:每次循环重复创建大量变量,且变量命名可能重复(球员名重复时),未及时回收资源,导致内存占用过高。
修复方案

以下是修正并优化后的代码:

import pandas as pd
from pulp import LpProblem, LpMaximize, LpVariable, lpSum, PULP_CBC_CMD
import psutil
import gc

# 内存监控函数
def get_memory_usage():
    process = psutil.Process()
    return f"{process.memory_info().rss / 1024 ** 2:.2f} MB"

dfs_proj = pd.read_csv("4for4_dfs_projections_120321.csv")
dfs_proj['count'] = 1
cols = ['Player', 'Pos', 'FFPts', 'DK ($)', 'count']
dfs_proj = dfs_proj[cols]
dfs_proj = dfs_proj[(dfs_proj['DK ($)'] >= 4000) | (dfs_proj['Pos'] == "DEF") | (dfs_proj['Pos'] == "TE")]

# 预处理位置标志列,避免重复计算
dfs_proj['is_QB'] = dfs_proj['Pos'].apply(lambda x: 1 if 'QB' in x else 0)
dfs_proj['is_DEF'] = dfs_proj['Pos'].apply(lambda x: 1 if 'DEF' in x else 0)
dfs_proj['is_RB'] = dfs_proj['Pos'].apply(lambda x: 1 if 'RB' in x else 0)
dfs_proj['is_WR'] = dfs_proj['Pos'].apply(lambda x: 1 if 'WR' in x else 0)
dfs_proj['is_TE'] = dfs_proj['Pos'].apply(lambda x: 1 if 'TE' in x else 0)

def get_optimals(site, data, num_lineups, optimize_on='FFPts'):
    lineups = []
    # 复制工作数据集,避免修改原数据
    working_data = data.copy().reset_index(drop=True)
    player_dict = dict(zip(working_data['Player'], working_data['count']))
    
    for lineup_num in range(1, num_lineups+1):
        print(f"Generating lineup {lineup_num}, current memory: {get_memory_usage()}")
        
        prob = LpProblem('DK_NFL_weekly', LpMaximize)
        # 用索引+球员名作为变量名,确保唯一性
        player_vars = {
            idx: LpVariable(f'Player_{idx}_{row.Player.replace(" ", "_")}', cat='Binary') 
            for idx, row in working_data.iterrows()
        }
        
        # 总人数约束
        prob += lpSum(player_vars.values()) == 9
        
        # 薪资约束
        salary_col = 'DK ($)' if site == 'DK' else 'FD ($)'
        prob += lpSum(working_data[salary_col].iloc[idx] * player_vars[idx] for idx in player_vars) <= 50000
        
        # 位置约束
        prob += lpSum(working_data['is_QB'].iloc[idx] * player_vars[idx] for idx in player_vars) == 1
        prob += lpSum(working_data['is_DEF'].iloc[idx] * player_vars[idx] for idx in player_vars) == 1
        prob += lpSum(working_data['is_RB'].iloc[idx] * player_vars[idx] for idx in player_vars) >= 2
        prob += lpSum(working_data['is_WR'].iloc[idx] * player_vars[idx] for idx in player_vars) >= 3
        prob += lpSum(working_data['is_TE'].iloc[idx] * player_vars[idx] for idx in player_vars) >= 1
        
        # 次优解约束
        if lineup_num > 1:
            prev_optimal = lineups[-1]['Total Points']
            if optimize_on == 'Optimal Frequency':
                prob += lpSum(working_data[optimize_on].iloc[idx] * player_vars[idx] for idx in player_vars) <= (prev_optimal - 0.001)
            else:
                prob += lpSum(working_data[optimize_on].iloc[idx] * player_vars[idx] for idx in player_vars) <= (prev_optimal - 0.01)
        
        # 目标函数
        prob += lpSum(working_data[optimize_on].iloc[idx] * player_vars[idx] for idx in player_vars)
        
        # 求解
        prob.solve(PULP_CBC_CMD(msg=False))
        current_optimal = prob.objective.value()
        
        # 提取阵容
        lineup = {'Total Points': current_optimal}
        count = 1
        for idx in player_vars:
            if player_vars[idx].value() == 1:
                row = working_data.iloc[idx]
                lineup[f'G{count}'] = row['Player']
                count += 1
        
        lineups.append(lineup)
        
        # 更新球员出场次数并移除达到上限的球员
        for player_key in [k for k in lineup if k.startswith('G')]:
            player = lineup[player_key]
            player_dict[player] += 1
            if player_dict[player] == 45:
                working_data = working_data[working_data['Player'] != player].reset_index(drop=True)
                player_dict.pop(player)
        
        # 回收内存
        del prob, player_vars
        gc.collect()
    
    return lineups

# 正确调用函数:参数顺序匹配定义
lineups = get_optimals('DK', dfs_proj, 20, 'FFPts')
关键优化点
  • 修正参数顺序:确保函数调用时参数与定义一致。
  • 预处理位置列:提前生成位置二进制标志,减少重复计算,提升效率。
  • 唯一变量命名:用索引+球员名(替换空格)作为变量名,避免冲突。
  • 内存监控与回收:添加内存监控函数,每次循环后删除无用对象并触发垃圾回收,降低内存占用。
  • 修复循环变量冲突:将外层循环变量改为lineup_num,避免与内层索引变量冲突。

内容的提问来源于stack exchange,提问作者Sam Hoppen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 02:54:22