基于神经网络的CTR实践

目录

一. 梗概

二. 实践代码

1. 库函数

2. 数据预处理

3. Feature Selection

4. 内存压缩

5. 训练集 + 测试集

6. 模型训练

7. 保存

三. 结论

1. 运行分析

2. 结果分析

 3. 结论分析

四. 备注


一. 梗概

        本次神经网络的建立基于之前LogisticRegression的实践过程,进行了例如数据预处理,特征工程,K-折交叉验证等特有的操作,考虑到内存占用的情况,同时加入了内存压缩函数,在原有基础上可压缩大约70%的内存空间,预计占用500M的内存,最终获取0.720802的成绩,排名246.

二. 实践代码

1. 库函数

# 安装必要的库


#安装相关依赖库 如果是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')

2. 数据预处理

# 进行数据预处理,类似于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()

3. Feature Selection

# 特征工程


# 自然数编码
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()函数的作用是计算对应指标列所有数据的均值

# 个人还没能完全理解此模块的作用

4. 内存压缩

# 内存压缩


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内存

5. 训练集 + 测试集

# 划分训练集和测试集


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()

6. 模型训练

# 模型训练


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")

7. 保存

# 结果保存


x_test['pctr'] = cat_test
x_test[['log_id','pctr']].to_csv('submission.csv', index=False) # 结果保存于submission的文件中,可在官网上提交结果查看对应分数

三. 结论

1. 运行分析

        通过该模型可在验证集上取得平均0.81的成绩,用时大约70min。

基于神经网络的CTR实践_第1张图片

2. 结果分析

        xAUC得分为0.720802,排名为246,目前第一名为0.874583(不断更新中)。

基于神经网络的CTR实践_第2张图片

 3. 结论分析

1. 特征提取部分可从指标之间的相关性进行具体分析,从而进行更进一步的筛选,提高泛化能力。

2. 个人认为代码不够简洁,可通过继承nn.Module类搭建更深的神经网络。

3. 可对超参数进行微调,但由于运行时间较长,需全面考虑之后再进行相关操作。

四. 备注

(前情回顾:基于LogisticRegression的CTR实践_Vector Jason的博客-CSDN博客)

你可能感兴趣的:(神经网络,推荐算法,深度学习,人工智能,线性回归)