这是什么情况啊?C:\Users\15713\Desktop\前端和项目\riiid-test-answer-prediction>python train.py Traceback (most recent call last): File "C:\Users\15713\Desktop\前端和项目\riiid-test-answer-prediction\train.py", line 17, in <module> train = pd.read_csv('train.csv', dtype=dtypes) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\io\parsers\readers.py", line 1026, in read_csv return _read(filepath_or_buffer, kwds) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\io\parsers\readers.py", line 626, in _read return parser.read(nrows) ^^^^^^^^^^^^^^^^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\io\parsers\readers.py", line 1968, in read df = DataFrame( ^^^^^^^^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\core\frame.py", line 778, in __init__ mgr = dict_to_mgr(data, index, columns, dtype=dtype, copy=copy, typ=manager) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\core\internals\construction.py", line 443, in dict_to_mgr arrays = Series(data, index=columns, dtype=object) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\core\series.py", line 537, in __init__ data, index = self._init_dict(data, index, dtype) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\core\series.py", line 651, in _init_dict s = Series(values, index=keys, dtype=dtype) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\core\series.py", line 584, in __init__ data = sanitize_array(data, index, dtype, copy) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\core\construction.py", line 651, in sanitize_array subarr = _try_cast(data, dtype, copy) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\core\construction.py", line 793, in _try_cast subarr = construct_1d_object_array_from_listlike(arr) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\core\dtypes\cast.py", line 1601, in construct_1d_object_array_from_listlike result[:] = values ~~~~~~^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\core\series.py", line 1031, in __array__ arr = np.asarray(values, dtype=dtype) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\core\arrays\masked.py", line 603, in __array__ return self.to_numpy(dtype=dtype) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Software\anaconda3\Lib\site-packages\pandas\core\arrays\masked.py", line 521, in to_numpy data = self._data.astype(dtype) ^^^^^^^^^^^^^^^^^^^^^^^^ numpy.core._exceptions._ArrayMemoryError: Unable to allocate 772. MiB for an array with shape (101230332,) and data type object,训练代码如下:import pandas as pd import numpy as np # 加载数据时指定类型节省内存 dtypes = { 'row_id': 'int64', 'timestamp': 'int64', 'user_id': 'int32', 'content_id': 'int16', 'content_type_id': 'int8', 'task_container_id': 'int16', 'user_answer': 'int8', 'answered_correctly': 'int8', 'prior_question_elapsed_time': 'float32', 'prior_question_had_explanation': 'boolean' } train = pd.read_csv('train.csv', dtype=dtypes) # 过滤掉讲座事件(只保留问题) train = train[train['content_type_id'] == 0].reset_index(drop=True) # 合并题目元数据 questions = pd.read_csv('questions.csv') train = train.merge(questions, left_on='content_id', right_on='question_id', how='left') # 处理空值 train['prior_question_elapsed_time'] = train['prior_question_elapsed_time'].fillna( train['prior_question_elapsed_time'].median()) train['prior_question_had_explanation'] = train['prior_question_had_explanation'].fillna(False) # 标签多热编码 tags_split = train['tags'].str.split(' ', expand=True) tags_dummies = pd.get_dummies(tags_split.stack()).groupby(level=0).max() train = pd.concat([train, tags_dummies], axis=1) # 用户累计正确率 user_correct = train.groupby('user_id')['answered_correctly'].agg(['mean', 'count']) user_correct.columns = ['user_hist_correct_rate', 'user_hist_question_count'] train = train.merge(user_correct, on='user_id', how='left') # 用户最近20题正确率(滑动窗口) train['user_recent_20_correct'] = train.groupby('user_id')['answered_correctly'].transform( lambda x: x.rolling(20, min_periods=1).mean() ) # 题目全局正确率 question_diff = train.groupby('question_id')['answered_correctly'].mean().reset_index() question_diff.columns = ['question_id', 'question_difficulty'] train = train.merge(question_diff, on='question_id', how='left') # 题目在用户所属分组的难度(如TOEIC part) part_diff = train.groupby('part')['answered_correctly'].mean().reset_index() part_diff.columns = ['part', 'part_avg_correct'] train = train.merge(part_diff, on='part', how='left') # 用户答题间隔时间变化率 train['time_diff_rate'] = train.groupby('user_id')['timestamp'].diff().fillna(0) / 1e3 # 转换为秒 # 用户当前任务容器与上次的时间差 train['task_container_gap'] = train.groupby('user_id')['task_container_id'].diff().fillna(0) import lightgbm as lgb from sklearn.model_selection import TimeSeriesSplit # 特征列选择 features = [ 'user_hist_correct_rate', 'user_hist_question_count', 'user_recent_20_correct', 'question_difficulty', 'part_avg_correct', 'prior_question_elapsed_time', 'prior_question_had_explanation', 'time_diff_rate', 'task_container_gap' ] + list(tags_dummies.columns) # 时间序列交叉验证 tscv = TimeSeriesSplit(n_splits=5) X = train[features] y = train['answered_correctly'] for fold, (train_idx, val_idx) in enumerate(tscv.split(X, y)): X_train, y_train = X.iloc[train_idx], y.iloc[train_idx] X_val, y_val = X.iloc[val_idx], y.iloc[val_idx] # 数据集转换 dtrain = lgb.Dataset(X_train, label=y_train) dval = lgb.Dataset(X_val, label=y_val) # 参数设置(优化NPU兼容性) # params = { # 'objective': 'binary', # 'metric': 'auc', # 'device': 'cpu', # 若使用英特尔NPU,需安装oneAPI优化版本 # 'num_leaves': 31, # 'learning_rate': 0.05, # 'feature_fraction': 0.8 # } # 定义 LightGBM 模型的参数,启用 GPU params = { 'objective': 'binary', 'metric': 'auc', 'device': 'gpu', # 启用 GPU 'gpu_device_id': 0, # 0 为 GTX 1050 的设备 ID,通常是 0 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.8 } # 训练 model = lgb.train(params, dtrain, valid_sets=[dval], callbacks=[lgb.log_evaluation(100)]) # 保存模型 model.save_model(f'lgbm_fold{fold}.txt')
嗯,用户遇到了一个内存错误,当运行他们的训练脚本时,出现了numpy.core._exceptions._ArrayMemoryError,提示无法分配772 MiB的数组。他们的代码是用来训练一个LightGBM模型的,处理Riiid测试答案预测的数据。我需要分析可能的原因并提供解决方案。
首先,错误发生在读取train.csv的时候,具体是pandas在读取数据时尝试分配一个大型数组失败。用户已经指定了dtypes来节省内存,但可能数据量太大,超出了可用内存。我需要看看他们处理数据的步骤是否有进一步优化的空间。
看一下他们的代码流程:加载数据,过滤讲座事件,合并题目元数据,处理空值,标签的多热编码,用户累计正确率,用户最近20题正确率,题目全局正确率,用户答题间隔时间等特征工程,然后进行LightGBM训练。
可能的问题点:在读取train.csv时,数据量太大,导致内存不足。即使用户指定了dtypes,如果原始数据文件train.csv本身很大,比如几GB甚至更大,加载到内存中可能会占用过多内存,尤其是在进行特征工程时,特别是多热编码部分,会生成很多新列,进一步增加内存使用。
可能的解决方案包括:
1. 使用更节省内存的数据类型,检查是否有列可以进一步优化,比如将某些int64转为更小的类型。例如,row_id可能不需要int64,可以试试int32或者更小的类型。但用户已经指定了dtypes,可能需要检查是否还有优化的空间。
2. 分块读取数据。使用pandas的chunksize参数,分批次处理数据,特别是特征工程部分,可能需要在每个块上处理,然后合并结果。但特征工程中有一些需要全局统计的操作,比如用户累计正确率,可能需要遍历所有数据,这时候分块处理可能会有挑战。
3. 减少内存使用的步骤,比如多热编码部分。如果tags_split产生很多列,可能会导致内存爆炸。例如,如果tags是很多不同的标签,每个标签都生成一个虚拟变量,那么tags_dummies的列数可能非常多,占用大量内存。这时候可能需要限制标签的数量,或者使用更高效的方法,如稀疏矩阵。
4. 使用更高效的数据格式,比如将数据转换为parquet格式,或者使用feather,可能会减少加载时的内存占用,但用户可能需要先转换数据,这可能对当前情况不太直接。
5. 使用Dask或者Vaex这样的库来处理大数据,但需要修改代码结构,可能会比较复杂。
6. 增加系统的虚拟内存,或者关闭其他占用内存的程序,释放更多内存。
具体来看用户的代码,在标签多热编码部分:
tags_split = train['tags'].str.split(' ', expand=True)
tags_dummies = pd.get_dummies(tags_split.stack()).groupby(level=0).max()
train = pd.concat([train, tags_dummies], axis=1)
这里,假设每个tags字段有多个标签,split之后每一行可能有多个标签,然后进行stack和get_dummies,这会生成很多列。例如,如果有成千上万个不同的标签,那么tags_dummies的列数会非常大,导致内存不足。例如,假设每个标签是一个整数,如果有1000个不同的标签,那么这里会增加1000列,每列是布尔类型。这会导致内存使用量激增。
这时候可以考虑是否有必要使用多热编码,或者是否有其他方式处理,比如将标签转换为数值特征,或者使用稀疏矩阵来存储。例如,使用scipy的稀疏矩阵,而不是pandas的DataFrame,可以节省内存。
另外,在用户累计正确率和题目全局正确率的计算中,可能也需要遍历整个数据集,如果数据太大,这些groupby操作也会占用大量内存。这时候可以考虑分块处理,或者使用更高效的计算方式。
另一个方面,用户可能在特征工程过程中生成了很多中间变量,比如合并后的questions,用户统计特征等。这些步骤可能会增加内存的使用,尤其是在合并后没有删除原始不需要的列的情况下。
可能的优化步骤:
1. 检查tags的数量,如果太多,考虑减少或分桶。或者使用稀疏格式存储多热编码的结果。例如,使用scipy.sparse的csr_matrix,然后转换为稀疏DataFrame(如果pandas支持的话,但pandas的稀疏DataFrame可能不如普通DataFrame节省内存)。或者,使用更高效的数据类型,比如uint8,如果每个dummy变量只有0和1,那么使用uint8会比bool更节省空间?不过pandas的get_dummies默认生成的是uint8类型,可能已经优化了。
或者,对于多热编码,如果标签数量太多,可能需要降维,比如使用PCA或者哈希编码,但可能影响模型效果。
2. 在读取数据时,使用更高效的方法。例如,使用usecols参数只加载需要的列,但用户可能需要所有列。或者,在读取时进行过滤,比如提前过滤掉content_type_id为0的行,这样在读取时就减少数据量。例如,在pd.read_csv中使用chunksize,逐块读取并过滤,然后合并。这样可以减少加载到内存中的数据量。
例如:
chunks = pd.read_csv('train.csv', dtype=dtypes, chunksize=100000)
filtered_chunks = []
for chunk in chunks:
filtered_chunk = chunk[chunk['content_type_id'] == 0]
filtered_chunks.append(filtered_chunk)
train = pd.concat(filtered_chunks, ignore_index=True)
这样在读取时逐块过滤,可以避免一次性加载全部数据到内存,减少内存峰值。
3. 对于特征工程中的groupby操作,比如用户累计正确率等,可以考虑使用更节省内存的方式,或者使用分块计算。例如,先对用户进行分组,分块处理每个用户的数据,累计统计。
4. 在每一步操作后,及时删除不再需要的中间变量,并用gc.collect()回收内存。例如,在合并questions后,如果不需要原始的questions DataFrame,可以删除它,或者使用inplace参数。
5. 将某些列转换为分类类型(category dtype),特别是像tags这样的列,如果有重复的字符串,转换为category可以节省内存。但需要注意,如果tags是空格分隔的多个标签,可能需要先拆分处理。
6. 使用更小的数值类型。例如,检查各个列的最大最小值,看是否可以将某些int32转为int16或者int8。例如,用户可能已经做了很多优化,但row_id可能被设置为int64,如果不需要这么大的范围,可以转成更小的类型。比如,如果row_id的最大值小于2^31,可以用int32。
7. 使用modin或pandas-on-ray等支持并行处理的库,但可能对内存帮助不大,反而可能增加开销。
回到错误信息,错误出现在读取train.csv的时候,用户可能内存不足以加载整个文件。即使指定了dtype,如果文件太大,比如超过可用内存,就会报错。这时候需要减少加载时的内存占用。
可能的解决方案顺序:
1. 在读取数据时进行过滤,使用chunksize参数,逐块读取并过滤掉content_type_id != 0的行,这样可以减少加载到内存中的数据量。例如:
chunks = pd.read_csv('train.csv', dtype=dtypes, chunksize=100000)
train_chunks = []
for chunk in chunks:
chunk = chunk[chunk['content_type_id'] == 0]
train_chunks.append(chunk)
train = pd.concat(train_chunks, ignore_index=True)
这样,在读取时逐块处理,可以避免一次性加载全部数据,特别是当原始数据包含大量content_type_id不为0的行时,过滤后数据量可能减少很多。
2. 检查tags的处理。如果tags的数量很多,生成的多热编码会导致内存问题。可以考虑将tags转换为更节省内存的方式。例如,如果tags是数值型的,可以分桶或者统计每个tag的出现次数,而不是进行多热编码。或者,使用稀疏矩阵:
from scipy.sparse import csr_matrix
tags_split = train['tags'].str.split(' ', expand=True)
stacked = tags_split.stack()
dummies = pd.get_dummies(stacked, sparse=True)
tags_dummies = dummies.groupby(level=0).max()
tags_sparse = csr_matrix(tags_dummies.values)
# 然后,将tags_sparse与其他特征合并可能需要转换其他特征为稀疏格式,这可能比较复杂。
或者,使用category类型:
tags_list = train['tags'].str.split(' ')
unique_tags = set(tag for sublist in tags_list.dropna() for tag in sublist)
for tag in unique_tags:
train[f'tag_{tag}'] = train['tags'].apply(lambda x: 1 if str(tag) in x.split() else 0)
但这种方法可能同样占用较多内存,如果tag数量很大。
另一种办法是统计每个用户的tag出现频率,或者每个问题的tag组合作为一个特征,但可能需要更复杂的处理。
3. 减少数据量。如果数据量实在太大,可以考虑对数据进行采样,比如随机采样部分用户或部分记录进行训练,虽然可能影响模型效果,但可以解决内存问题。
4. 使用Dask来处理数据。Dask可以处理超过内存的数据集,通过并行和分块处理。例如:
import dask.dataframe as dd
train = dd.read_csv('train.csv', dtype=dtypes)
train = train[train['content_type_id'] == 0].compute()
但需要足够的磁盘空间和适当的分块设置,否则可能在compute()时仍然需要大量内存。
5. 优化其他特征工程步骤的内存使用:
- 在合并questions后,删除原始的questions DataFrame。
- 将不需要的列及时删除,例如在合并后,如果不需要某些列,使用drop方法。
- 在分组统计时,使用更高效的方法,例如transform可能生成副本,考虑是否可以用更高效的方式计算。
- 将某些对象类型的列转换为category类型,比如'prior_question_had_explanation'已经被指定为boolean,可能已经是优化过的。
6. 检查系统内存是否足够。用户是否在内存较小的机器上运行?例如,如果可用内存只有8GB,而数据需要10GB,那么需要扩展内存或使用其他方法。
总结,最直接的解决方案可能是在读取数据时进行分块处理,并在读取时过滤掉不需要的行。这样可以有效减少内存使用。同时,检查tags的处理,避免生成过多的列,导致内存爆炸。如果必须处理大量标签,考虑使用稀疏矩阵或其他降维方法。
根据错误信息,你的程序在读取训练数据时因内存不足而崩溃。以下是分步解决方案:
1. 分块读取并过滤数据
python
复制
# 修改数据读取部分为分块读取 chunk_size = 100000 # 根据内存情况调整 chunks = pd.read_csv('train.csv', dtype=dtypes, chunksize=chunk_size) filtered_chunks = [] for chunk in chunks: # 立即过滤不需要的行 chunk = chunk[chunk['content_type_id'] == 0] # 立即删除原始列释放内存 chunk.drop(columns=['content_type_id'], inplace=True) filtered_chunks.append(chunk) train = pd.concat(filtered_chunks, ignore_index=True)2. 优化标签处理(内存消耗最大的部分)
python
复制
# 使用稀疏矩阵处理标签 from scipy.sparse import csr_matrix # 预处理tags列 tags_series = train['tags'].astype(str).str.split() unique_tags = set(tag for sublist in tags_series for tag in sublist) # 创建稀疏矩阵 row_indices = [] col_indices = [] for i, tags in enumerate(tags_series): for tag in tags: col_idx = list(unique_tags).index(tag) row_indices.append(i) col_indices.append(col_idx) tags_sparse = csr_matrix(([1]*len(row_indices), (row_indices, col_indices)) # 将稀疏矩阵转换为DataFrame(可选) tags_df = pd.DataFrame.sparse.from_spmatrix(tags_sparse, columns=unique_tags) train = pd.concat([train, tags_df], axis=1)3. 优化内存使用的其他技巧
python
复制
# 在每一步操作后释放内存 import gc # 合并题目元数据后 del questions gc.collect() # 处理空值后 train['prior_question_elapsed_time'] = train['prior_question_elapsed_time'].astype('float32') train['prior_question_had_explanation'] = train['prior_question_had_explanation'].astype('bool') # 删除中间变量 del tags_series, tags_sparse, tags_df gc.collect()4. 特征工程优化
python
复制
# 使用更高效的分组统计方法 # 用户累计正确率(使用transform避免创建中间对象) train['user_hist_correct_rate'] = train.groupby('user_id')['answered_correctly'].transform('mean') train['user_hist_question_count'] = train.groupby('user_id')['answered_correctly'].transform('count') # 使用更高效的时间差计算 train['task_container_gap'] = train.groupby('user_id')['task_container_id'].diff().astype('int16')5. 最终内存优化
python
复制
# 转换所有可能列的数值类型 float_cols = train.select_dtypes(include=['float64']).columns train[float_cols] = train[float_cols].astype('float32') int_cols = train.select_dtypes(include=['int64']).columns train[int_cols] = train[int_cols].astype('int32') # 删除原始标签列 train.drop(columns=['tags'], inplace=True)补充建议:
使用
memory_profiler
分析内存使用尝试将数据转换为parquet格式后再读取
如果GPU显存不足,改回CPU训练:
python
复制
params = { 'objective': 'binary', 'metric': 'auc', 'device': 'cpu', # 使用CPU 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.8 }
考虑使用Dask处理超大数据集:
python
复制
import dask.dataframe as dd train = dd.read_csv('train.csv', dtype=dtypes) train = train[train['content_type_id'] == 0] train = train.compute() # 转换为pandas DataFrame这些修改应该能显著降低内存使用量。如果仍然遇到内存问题,可以考虑:
使用更强大的云服务器
对数据进行降采样
使用特征选择减少特征数量
使用更紧凑的模型架构(如神经网络嵌入)