目录
一. 梗概
二. 实践代码
1. 库函数
2. 数据预处理
3. Feature Selection
4. 内存压缩
5. 训练集 + 测试集
6. 模型训练
7. 保存
三. 结论
1. 运行分析
2. 结果分析
3. 结论分析
四. 备注
本次神经网络的建立基于之前LogisticRegression的实践过程,进行了例如数据预处理,特征工程,K-折交叉验证等特有的操作,考虑到内存占用的情况,同时加入了内存压缩函数,在原有基础上可压缩大约70%的内存空间,预计占用500M的内存,最终获取0.720802的成绩,排名246.
# 安装必要的库
#安装相关依赖库 如果是windows系统,cmd命令框中输入pip安装,参考上述环境配置
#!pip install sklearn
#!pip install pandas
#!pip install catboost
#---------------------------------------------------
#导入库
#----------------数据探索----------------
import pandas as pd
import numpy as np
import os
import gc
import matplotlib.pyplot as plt
from tqdm import *
#----------------核心模型----------------
from catboost import CatBoostClassifier
from sklearn.linear_model import SGDRegressor, LinearRegression, Ridge
#----------------交叉验证----------------
from sklearn.model_selection import StratifiedKFold, KFold
#----------------评估指标----------------
from sklearn.metrics import accuracy_score, f1_score, roc_auc_score, log_loss
#----------------忽略报警----------------
import warnings
warnings.filterwarnings('ignore')
# 进行数据预处理,类似于Dataset类的作用
# 利用pands第三方库读取训练数据和测试数据,其中,data_ads取自目标域(广告)中的数据,data_feeds取自源域(同一媒体的跨域)后的数据,
train_data_ads = pd.read_csv('./train/train_data_ads.csv')
train_data_feeds = pd.read_csv('./train/train_data_feeds.csv')
test_data_ads = pd.read_csv('./test/test_data_ads.csv')
test_data_feeds = pd.read_csv('./test/test_data_feeds.csv')
# 合并数据
# 合并数据
train_data_ads['istest'] = 0 # 由于是train的数据,因此'istest' = 0
test_data_ads['istest'] = 1 # 由于是test的数据,因此'istest' = 1
data_ads = pd.concat([train_data_ads, test_data_ads], axis=0, ignore_index=True) # 利用pands库中的concat函数将train_data_ads, test_data_ads两个文件按行合并
train_data_feeds['istest'] = 0
test_data_feeds['istest'] = 1
data_feeds = pd.concat([train_data_feeds, test_data_feeds], axis=0, ignore_index=True) # 操作同上
del train_data_ads, test_data_ads, train_data_feeds, test_data_feeds
gc.collect()
# 特征工程
# 自然数编码
def label_encode(series, series2):
unique = list(series.unique())
return series2.map(dict(zip(
unique, range(series.nunique())
)))
for col in ['ad_click_list_v001','ad_click_list_v002','ad_click_list_v003','ad_close_list_v001','ad_close_list_v002','ad_close_list_v003','u_newsCatInterestsST']:
data_ads[col] = label_encode(data_ads[col], data_ads[col])
# 具体查阅数据,部分数据形式表现为字符串string,所以此处需要以one-hot vector的形式来表现此类数据,故使用自然数编码对相关列进行处理
# data_feeds特征构建(可考虑通过指标之间的相关性进行更进一步的修改)
cols = [f for f in data_feeds.columns if f not in ['label','istest','u_userId']]
for col in tqdm(cols):
tmp = data_feeds.groupby(['u_userId'])[col].nunique().reset_index()
tmp.columns = ['user_id', col+'_feeds_nuni']
data_ads = data_ads.merge(tmp, on='user_id', how='left')
# 其中nunique()函数的作用是计算不同数据的个数个人还没能完全理解此处的作用
cols = [f for f in data_feeds.columns if f not in ['istest','u_userId','u_newsCatInterests','u_newsCatDislike','u_newsCatInterestsST','u_click_ca2_news','i_docId','i_s_sourceId','i_entities']]
for col in tqdm(cols):
tmp = data_feeds.groupby(['u_userId'])[col].mean().reset_index()
tmp.columns = ['user_id', col+'_feeds_mean']
data_ads = data_ads.merge(tmp, on='user_id', how='left')
# 其中mean()函数的作用是计算对应指标列所有数据的均值
# 个人还没能完全理解此模块的作用
# 内存压缩
def reduce_mem_usage(df, verbose=True):
numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
start_mem = df.memory_usage().sum() / 1024**2
for col in df.columns:
col_type = df[col].dtypes
if col_type in numerics:
c_min = df[col].min()
c_max = df[col].max()
if str(col_type)[:3] == 'int':
if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
df[col] = df[col].astype(np.int8)
elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
df[col] = df[col].astype(np.int16)
elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
df[col] = df[col].astype(np.int32)
elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
df[col] = df[col].astype(np.int64)
else:
if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
df[col] = df[col].astype(np.float16)
elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
df[col] = df[col].astype(np.float32)
else:
df[col] = df[col].astype(np.float64)
end_mem = df.memory_usage().sum() / 1024**2
if verbose: print('Mem. usage decreased to {:5.2f} Mb ({:.1f}% reduction)'.format(end_mem, 100 * (start_mem - end_mem) / start_mem))
return df
# 压缩使用内存
data_ads = reduce_mem_usage(data_ads)
# Mem. usage decreased to 2351.47 Mb (69.3% reduction)
# 对于压缩内存代码部分,个人对该部分完全不理解,但可以肯定的是,如果去掉此部分,将占用电脑大约10G的内存(相当于下载一个游戏了),因此此处进行了借鉴
# reduce_mem_usage函数可以压缩近70%的内存占有,运行下来预计占用500M内存
# 划分训练集和测试集
cols = [f for f in data_ads.columns if f not in ['label','istest']]
x_train = data_ads[data_ads.istest==0][cols] # 将处理后的data_ads中istest==0的部分划分为训练集
x_test = data_ads[data_ads.istest==1][cols] # 将处理后的data_ads中istest==1的部分划分为测试集
y_train = data_ads[data_ads.istest==0]['label'] # 定义训练集中的label
del data_ads, data_feeds
gc.collect()
# 模型训练
def cv_model(clf, train_x, train_y, test_x, clf_name, seed=2022):
kf = KFold(n_splits=5, shuffle=True, random_state=seed) # 此处定义K-折交叉验证的参数
train = np.zeros(train_x.shape[0])
test = np.zeros(test_x.shape[0])
cv_scores = []
for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
print('************************************ {} {}************************************'.format(str(i+1), str(seed)))
trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]
params = {'learning_rate': 0.3, 'depth': 5, 'l2_leaf_reg': 10, 'bootstrap_type':'Bernoulli','random_seed':seed,
'od_type': 'Iter', 'od_wait': 50, 'random_seed': 11, 'allow_writing_files': False} # 定义超参数,可进行微调
model = clf(iterations=20000, **params, eval_metric='AUC')
model.fit(trn_x, trn_y, eval_set=(val_x, val_y),
metric_period=200,
cat_features=[],
use_best_model=True,
verbose=1)
val_pred = model.predict_proba(val_x)[:,1]
test_pred = model.predict_proba(test_x)[:,1]
train[valid_index] = val_pred
test += test_pred / kf.n_splits
cv_scores.append(roc_auc_score(val_y, val_pred))
print(cv_scores)
print("%s_score_list:" % clf_name, cv_scores)
print("%s_score_mean:" % clf_name, np.mean(cv_scores))
print("%s_score_std:" % clf_name, np.std(cv_scores))
return train, test
cat_train, cat_test = cv_model(CatBoostClassifier, x_train, y_train, x_test, "cat")
# 结果保存
x_test['pctr'] = cat_test
x_test[['log_id','pctr']].to_csv('submission.csv', index=False) # 结果保存于submission的文件中,可在官网上提交结果查看对应分数
通过该模型可在验证集上取得平均0.81的成绩,用时大约70min。
xAUC得分为0.720802,排名为246,目前第一名为0.874583(不断更新中)。
1. 特征提取部分可从指标之间的相关性进行具体分析,从而进行更进一步的筛选,提高泛化能力。
2. 个人认为代码不够简洁,可通过继承nn.Module类搭建更深的神经网络。
3. 可对超参数进行微调,但由于运行时间较长,需全面考虑之后再进行相关操作。
(前情回顾:基于LogisticRegression的CTR实践_Vector Jason的博客-CSDN博客)