{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.model_selection import train_test_split\nimport joblib\n\ndef sparse_faeture_dict(feat_name, feat_num, embed_dim=4):\n    \"\"\"\n    为每个离散变量建立信息字典\n    :param feat_name: 特征名。\n    :param feat_num: 特征数，每一个特征编码后对应有多少个类别。\n    :param embed_dim: 特征维度，特征embedding后的维度。\n    :return:\n    \"\"\"\n    return {\"feat_name\": feat_name, \"feat_num\": feat_num, \"embed_dim\": embed_dim}\n\ndef create_avazu_dataset(path, read_part=True, samples_num=5000, embed_dim=8):\n    print('数据预处理开始')\n    sparse_features = ['hour', 'id', 'C1', 'banner_pos', 'site_id', 'site_domain',\n                       'site_category', 'app_id', 'app_domain', 'app_category', 'device_id',\n                       'device_ip', 'device_model', 'device_type', 'device_conn_type', 'C14',\n                       'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21']\n\n    train_path = path + '/train.gz'\n    test_path = path + '/test.gz'\n    print('加载数据集')\n    if read_part:\n        train_data = pd.read_csv(train_path, nrows=samples_num)\n    else:\n        train_data = pd.read_csv(train_path)\n    test_x = pd.read_csv(test_path)\n\n    # hour, 只有14年10月11天的数据，year,month没必要做特征\n    train_data['hour'] = train_data['hour'].apply(str)\n    train_data['hour'] = train_data['hour'].map(lambda x: int(x[6:8]))  # int强转去掉字符串前的0\n    test_x['hour'] = test_x['hour'].apply(str)\n    test_x['hour'] = test_x['hour'].map(lambda x: int(x[6:8]))\n    print('加载数据完成')\n    print('Sparse feature encode')\n    # sparse features\n    le = LabelEncoder()\n    for feat in sparse_features:\n        all_class = pd.concat([train_data[feat], test_x[feat]]).unique()\n        le.fit(all_class)\n        train_data[feat] = le.transform(train_data[feat])\n        test_x[feat] = le.transform(test_x[feat])\n\n    print('Sparse feature encode succeed')\n    # save LabelEncoder model for test\n    # joblib.dump(le, 'label_encoder.model')\n    # sparse_faeture_dict(feat_name='day', feat_num=32, embed_dim=embed_dim)\n    # sparse_faeture_dict(feat_name='hour', feat_num=24, embed_dim=embed_dim)\n    features_columns = [sparse_faeture_dict(feat_name=feat, feat_num=train_data[feat].max() + 1, embed_dim=embed_dim)\n                        for feat in sparse_features]\n\n    train, val = train_test_split(train_data, test_size=0.2, shuffle=True)\n    train_x = train[sparse_features].values.astype('int32')\n    train_y = train['click'].values.astype('int32')\n    val_x = val[sparse_features].values.astype('int32')\n    val_y = val['click'].values.astype('int32')\n    test_x = test_x[sparse_features].values.astype('int32')\n\n    print('数据预处理完成')\n    return (train_x, train_y), (val_x, val_y), test_x, features_columns","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-04-25T01:39:01.315255Z","iopub.execute_input":"2022-04-25T01:39:01.315526Z","iopub.status.idle":"2022-04-25T01:39:01.332421Z","shell.execute_reply.started":"2022-04-25T01:39:01.315496Z","shell.execute_reply":"2022-04-25T01:39:01.331620Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.keras.layers import Layer\n\n\nclass KMaxPooling(Layer):\n    def __init__(self, k, dim):\n        super(KMaxPooling, self).__init__()\n        self.k = k\n        self.dim = dim\n\n    def forward(self, X):\n        index = X.topk(self.k, dim=self.dim)[1].sort(dim=self.dim)[0]\n        output = X.gather(self.dim, index)\n        return output\n    \nfrom tensorflow.keras.regularizers import l2\nfrom tensorflow.keras import Model\nfrom tensorflow.keras.layers import Embedding, Conv2D, ZeroPadding2D, Dense, Flatten\nfrom tensorflow.keras.initializers import RandomNormal\nimport tensorflow as tf\nfrom tensorflow.keras.activations import relu, softmax, sigmoid\n\n\nclass CCPM(Model):\n    \"\"\"\n    \"\"\"\n\n    def __init__(self, feat_column, conv_kernel_width=(6, 5, 3), conv_filters=(4, 4, 4), embed_reg=1e-6):\n        super(CCPM, self).__init__()\n        self.sparse_feat = feat_column\n        self.sparse_feat_len = len(self.sparse_feat)\n        self.conv_len = len(conv_filters)  # 卷积层数\n\n        self.embedding_list = [\n            Embedding(input_dim=feat['feat_num'], output_dim=feat['embed_dim'],\n                      embeddings_initializer=RandomNormal(mean=0.0, stddev=0.0001, seed=2020),\n                      embeddings_regularizer=l2(embed_reg), input_length=1)\n            for feat in self.sparse_feat]\n\n        # KMaxPooling\n        self.p = []\n        for i in range(1, self.conv_len + 1):\n            if i < self.conv_len:\n                k = max(1, int((1 - pow(i / self.conv_len, self.conv_len - i)) * self.sparse_feat_len))\n                self.p.append(k)\n            else:\n                self.p.append(3)\n        self.max_pooling_list = [KMaxPooling(k, dim=2) for k in self.p]\n\n        self.padding_list = [ZeroPadding2D(padding=(0, conv_kernel_width[i] - 1))\n                             for i in range(self.conv_len)]\n        self.conv_list = [Conv2D(filters=conv_filters[i], kernel_size=(1, conv_kernel_width[i]))\n                          for i in range(self.conv_len)]\n\n        self.flatten = Flatten()\n        self.dense = Dense(units=1)\n\n    def call(self, inputs, training=None, mask=None):\n        # batch,feat_num\n        sparse_feat = inputs\n        # batch,embed_dim,feat_num\n        s = tf.stack([self.embedding_list[i](sparse_feat[:, i]) for i in range(self.sparse_feat_len)], axis=-1)\n        # 先扩充channel维度\n        s = tf.expand_dims(s, axis=3)\n\n        for i in range(self.conv_len):\n            # padding\n            s = self.padding_list[i](s)\n            # conv , batch,embed_dim,width,channel\n            r = self.conv_list[i](s)\n            s = self.max_pooling_list[i](r)\n            s = relu(s)\n\n        outputs = self.dense(self.flatten(s))\n        outputs = sigmoid(outputs)\n        return outputs\n","metadata":{"execution":{"iopub.status.busy":"2022-04-25T01:39:01.334449Z","iopub.execute_input":"2022-04-25T01:39:01.334705Z","iopub.status.idle":"2022-04-25T01:39:01.357209Z","shell.execute_reply.started":"2022-04-25T01:39:01.334672Z","shell.execute_reply":"2022-04-25T01:39:01.356512Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.losses import binary_crossentropy\nfrom tensorflow.keras.callbacks import EarlyStopping\n\npath = '../input/avazu-ctr-prediction/'\n\nembed_dim = 11\n\nlearning_rate = 0.001\nbatch_size = 128\nepochs = 20\n\n(train_x, train_y), (val_x, val_y), test_x, features_columns = create_avazu_dataset(path, read_part=True, samples_num=10000000,\n                                                                            embed_dim=embed_dim)\n\nmodel = CCPM(feat_column=features_columns)\nmodel.compile(optimizer=Adam(), loss=binary_crossentropy)\nmodel.fit(x=train_x, y=train_y, batch_size=batch_size, epochs=epochs, validation_split=0.1, shuffle=True,callbacks=[EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True)])\nresult = model.predict(test_x,batch_size=10000)\npd.Series(result.flatten()).to_csv('result.csv',header=None,index=False)\n","metadata":{"execution":{"iopub.status.busy":"2022-04-25T01:39:01.358766Z","iopub.execute_input":"2022-04-25T01:39:01.359303Z","iopub.status.idle":"2022-04-25T01:41:59.069391Z","shell.execute_reply.started":"2022-04-25T01:39:01.359265Z","shell.execute_reply":"2022-04-25T01:41:59.067334Z"},"trusted":true},"execution_count":null,"outputs":[]}]}