特征工程技巧——字符串编码成数字序列

这段时间在参加比赛,发现有一些比赛上公开的代码,其中的数据预处理步骤值得我们参考。

平常我们见到的都是数据预处理,现在我们来讲一下特征工程跟数据预处理的区别。

  1. 数据预处理是指对原始数据进行清洗、转换、缩放等操作,以便为后续的建模或分析任务做准备。这包括处理缺失值、异常值、重复值,以及对数据进行归一化、标准化等操作,使数据适合模型处理。

  2. 特征工程则更侧重于从原始数据中提取、构建或转换特征,以提高模型的性能。这包括特征选择、特征抽取、特征转换等过程。在特征工程中,可以创建新的特征、组合现有特征、进行降维等操作,以便使模型更好地捕捉数据中的模式和关系

 训练集

测试集

注:这个代码也可以用在自己的工程项目中,还是比较不错的!也是目前在Kaggle社区里公开代码分数比较高的一个单模型。

在这个代码里,我们将训练使用所有的数据可用的深度学习模型!

预处理: 我把所有训练集的和测试集的smiles编码并保存在这里,这可能需要1个小时的 TPU。

训练与推理: 我使用了一个简单的1dcnn 模型对20个epochs进行训练。

如何改进:

尝试不同的体系结构: 我能够得到0.604的 LB 分数,只需对这个体系结构进行一些小的更改。

尝试另一个模型,如Transformer,或 LSTM。

为更多的epochs而训练。

添加更多特性,比如 bb2或 bb3的热编码。

当然还有 GBDT 模型。

1、导入相应的包 

!pip install fastparquet -q

fastparquet用于读写Parquet格式的数据文件。 

2、导入库

import gc
import os
import pickle
import random
import joblib
import numpy as np
import pandas as pd
from tqdm import tqdm
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import average_precision_score as APS

 这段代码导入了必要的Python库,包括用于数据处理和建模的常见库,例如pandas、numpy、scikit-learn等。它还使用了tqdm来显示进度条。

3、定义配置参数

class CFG:

    PREPROCESS = False
    EPOCHS = 100
    BATCH_SIZE = 4096
    LR = 1e-3
    WD = 0.05

    NBR_FOLDS = 15
    SELECTED_FOLDS = [0]

    SEED = 2024

这里定义了一个名为CFG的类,用于存储一些全局配置参数。这些参数包括是否进行数据预处理、训练时的迭代次数、批量大小、学习率、权重衰减等。NBR_FOLDS表示交叉验证的折数,SELECTED_FOLDS表示选择参与训练的折数,SEED是随机种子。这里我们表示只选择第一份数据作为验证集,其余14份数据作为训练集。

4、设置随机数种子

import tensorflow as tf
def set_seeds(seed):
    os.environ['PYTHONHASHSEED'] = str(seed)
    random.seed(seed)
    tf.random.set_seed(seed)
    np.random.seed(seed)

set_seeds(seed=CFG.SEED)

set_seeds()函数用于设置随机种子,确保实验的可重复性。它设置了Python、NumPy、random和TensorFlow的随机种子。 

注:str是用于字符串操作的内置数据类型,而os.environ是用于管理操作系统环境变量的模块对象。在这段代码中,str(seed)用于将整数转换为字符串,os.environ['PYTHONHASHSEED']用于设置Python的哈希种子。

5、分布式策略

import tensorflow as tf

# Detect hardware, return appropriate distribution strategy
try:
    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect(tpu="local") # "local" for 1VM TPU
    strategy = tf.distribute.TPUStrategy(tpu)
    print("Running on TPU")
    print("REPLICAS: ", strategy.num_replicas_in_sync)
except tf.errors.NotFoundError:
    print("Not on TPU")

这段代码尝试连接到TPU(如果可用),并创建了一个分布式策略strategy,以便在TPU上进行训练。如果没有TPU可用,则打印"Not on TPU"。strategy.num_replicas_in_sync用于获取分布式策略中的副本数。 

tf.distribute.cluster_resolver.TPUClusterResolver 是 TensorFlow 中用于连接 TPU(Tensor Processing Unit,张量处理单元)集群的类。下面是对其功能的解释:

  • 连接到TPU集群:TPUClusterResolver 的主要作用是连接到 TPU 集群,以便在该集群上执行 TensorFlow 计算任务。

  • **指定TPU名称:**你可以通过 TPUClusterResolver 类来指定要连接的 TPU 的名称,可以是本地的 TPU(local),也可以是远程的 TPU。例如,在上述代码中,使用了 tpu="local" 来指定连接到本地的 TPU。

  • **解析TPU集群地址:**除了连接到指定的 TPU 外,TPUClusterResolver 还可以解析 TPU 集群的地址,以便连接到指定地址的 TPU 集群。这对于连接到远程的 TPU 集群非常有用。

  • **获取TPU实例信息:**一旦连接到了 TPU 集群,TPUClusterResolver 还可以提供关于 TPU 实例的信息,例如 TPU 的数量、类型等。

在上述代码中,TPUClusterResolver 被用于连接到本地的 TPU(tpu="local")。如果连接成功,将返回一个 TPUClusterResolver 对象,否则会抛出异常。连接成功后,可以使用该对象来获取有关 TPU 集群的信息,并将其用于 TensorFlow 的分布式计算任务。我这里用的是云服务器上的TPU。

tf.distribute.TPUStrategy 是 TensorFlow 中专门用于在 TPU 上进行分布式训练的策略。类似地,TensorFlow 也提供了用于在 CPU 和 GPU 上进行分布式训练的相应策略。

  • **tf.distribute.MirroredStrategy:**用于在多个 GPU 上进行分布式训练。每个 GPU 上的模型副本会在各自的 GPU 上运行,同时使用 AllReduce 算法来进行梯度聚合和参数同步。

  • **tf.distribute.MultiWorkerMirroredStrategy:**用于在多个工作节点(通常是多台机器)的多个 GPU 上进行分布式训练。它在 MirroredStrategy 的基础上增加了多个工作节点之间的通信和同步。

  • **tf.distribute.experimental.MultiDeviceMirroredStrategy:**用于在多个 GPU 或 CPU 上进行分布式训练。与 MirroredStrategy 类似,但允许跨设备进行梯度和参数同步。

  • **tf.distribute.experimental.CentralStorageStrategy:**用于在多个工作节点的多个 GPU 或 CPU 上进行分布式训练。与 MirroredStrategy 类似,但使用中心化参数服务器来进行梯度聚合和参数同步。

这些策略都允许在不同的硬件设备上进行分布式训练,包括 CPU、GPU 和 TPU。选择合适的策略取决于你的硬件环境、任务需求和性能要求。例如,如果你拥有多个 GPU,并且想要在这些 GPU 上进行训练,可以选择 MirroredStrategy;如果你想要在多台机器上进行训练,则可以选择 MultiWorkerMirroredStrategy

6、数据预处理

if CFG.PREPROCESS:
    enc = {'l': 1, 'y': 2, '@': 3, '3': 4, 'H': 5, 'S': 6, 'F': 7, 'C': 8, 'r': 9, 's': 10, '/': 11, 'c': 12, 'o': 13,
           '+': 14, 'I': 15, '5': 16, '(': 17, '2': 18, ')': 19, '9': 20, 'i': 21, '#': 22, '6': 23, '8': 24, '4': 25, '=': 26,
           '1': 27, 'O': 28, '[': 29, 'D': 30, 'B': 31, ']': 32, 'N': 33, '7': 34, 'n': 35, '-': 36}
    train_raw = pd.read_parquet('/kaggle/input/leash-BELKA/train.parquet')
    smiles = train_raw[train_raw['protein_name']=='BRD4']['molecule_smiles'].values
    assert (smiles!=train_raw[train_raw['protein_name']=='HSA']['molecule_smiles'].values).sum() == 0
    assert (smiles!=train_raw[train_raw['protein_name']=='sEH']['molecule_smiles'].values).sum() == 0
    def encode_smile(smile):
        tmp = [enc[i] for i in smile]
        tmp = tmp + [0]*(142-len(tmp))
        return np.array(tmp).astype(np.uint8)

    smiles_enc = joblib.Parallel(n_jobs=96)(joblib.delayed(encode_smile)(smile) for smile in tqdm(smiles))
    smiles_enc = np.stack(smiles_enc)
    train = pd.DataFrame(smiles_enc, columns = [f'enc{i}' for i in range(142)])
    train['bind1'] = train_raw[train_raw['protein_name']=='BRD4']['binds'].values
    train['bind2'] = train_raw[train_raw['protein_name']=='HSA']['binds'].values
    train['bind3'] = train_raw[train_raw['protein_name']=='sEH']['binds'].values
    train.to_parquet('train_enc.parquet')

    test_raw = pd.read_parquet('/kaggle/input/leash-BELKA/test.parquet')
    smiles = test_raw['molecule_smiles'].values

    smiles_enc = joblib.Parallel(n_jobs=96)(joblib.delayed(encode_smile)(smile) for smile in tqdm(smiles))
    smiles_enc = np.stack(smiles_enc)
    test = pd.DataFrame(smiles_enc, columns = [f'enc{i}' for i in range(142)])
    test.to_parquet('test_enc.parquet')

else:
    train = pd.read_parquet('/kaggle/input/belka-enc-dataset/train_enc.parquet')
    test = pd.read_parquet('/kaggle/input/belka-enc-dataset/test_enc.parquet')

如果CFG.PREPROCESS为True,则执行数据预处理。否则,直接从parquet文件中读取处理好的训练和测试数据。 

enc = {...}:这里定义了一个字典 enc,将 SMILES 字符串中的字符映射为整数编码。这个编码过程是为了将 SMILES 字符串转换为数字序列。

train_raw[train_raw['protein_name']=='BRD4'] 这部分代码实际上是在利用 Pandas DataFrame 的索引功能。让我们解释一下它是如何工作的:

  1. train_raw['protein_name']=='BRD4':这部分代码首先选择了 train_raw DataFrame 中的 protein_name 列,并将其与字符串 'BRD4' 进行比较。这将返回一个布尔数组,其中对应的位置为 True 表示该位置对应的蛋白质名称为 'BRD4',否则为 False。例如,如果 train_raw['protein_name'] 的值分别是 ['BRD4', 'HSA', 'BRD4', 'sEH', 'HSA'],那么上述代码返回的布尔数组就是 [True, False, True, False, False]

  2. train_raw[train_raw['protein_name']=='BRD4']:这部分代码将上一步得到的布尔数组作为索引,从 train_raw DataFrame 中选择出满足条件的行。具体来说,它保留了布尔数组中对应位置为 True 的行,而过滤掉了对应位置为 False 的行。这样,就实现了根据条件筛选数据的目的。

综上所述,train_raw[train_raw['protein_name']=='BRD4'] 的含义是:从 train_raw DataFrame 中选择出满足条件(蛋白质名称为 'BRD4')的所有行。

np.stack() 函数的作用是沿着新的轴堆叠数组序列。当你有一系列形状相同的数组时,你可以使用 np.stack() 将它们沿着新的轴堆叠起来,形成一个新的数组。

7、定义神经网络模型

def my_model():
    with strategy.scope():
        INP_LEN = 142
        NUM_FILTERS = 32
        hidden_dim = 128

        inputs = tf.keras.layers.Input(shape=(INP_LEN,), dtype='int32')
        x = tf.keras.layers.Embedding(input_dim=36, output_dim=hidden_dim, input_length=INP_LEN, mask_zero = True)(inputs)
        x = tf.keras.layers.Conv1D(filters=NUM_FILTERS, kernel_size=3,  activation='relu', padding='valid',  strides=1)(x)
        x = tf.keras.layers.Conv1D(filters=NUM_FILTERS*2, kernel_size=3,  activation='relu', padding='valid',  strides=1)(x)
        x = tf.keras.layers.Conv1D(filters=NUM_FILTERS*3, kernel_size=3,  activation='relu', padding='valid',  strides=1)(x)
        x = tf.keras.layers.GlobalMaxPooling1D()(x)

        x = tf.keras.layers.Dense(1024, activation='relu')(x)
        x = tf.keras.layers.Dropout(0.1)(x)
        x = tf.keras.layers.Dense(1024, activation='relu')(x)
        x = tf.keras.layers.Dropout(0.1)(x)
        x = tf.keras.layers.Dense(512, activation='relu')(x)
        x = tf.keras.layers.Dropout(0.1)(x)

        outputs = tf.keras.layers.Dense(3, activation='sigmoid')(x)

        model = tf.keras.models.Model(inputs = inputs, outputs = outputs)
        optimizer = tf.keras.optimizers.Adam(learning_rate=CFG.LR, weight_decay = CFG.WD)
        loss = 'binary_crossentropy'
        weighted_metrics = [tf.keras.metrics.AUC(curve='PR', name = 'avg_precision')]
        model.compile(
        loss=loss,
        optimizer=optimizer,
        weighted_metrics=weighted_metrics,
        )
        return model

 模型详解

    with strategy.scope():

 TensorFlow 中的一种分布式策略,strategy.scope() 用于在分布式环境中构建模型。

        INP_LEN = 142
        NUM_FILTERS = 32
        hidden_dim = 128

 定义了一些常量和超参数,如输入序列长度 INP_LEN、卷积层滤波器数量 NUM_FILTERS 和嵌入层的隐藏维度 hidden_dim

        inputs = tf.keras.layers.Input(shape=(INP_LEN,), dtype='int32')

 定义了模型的输入层,指定了输入序列的形状和数据类型。

        x = tf.keras.layers.Embedding(input_dim=36, output_dim=hidden_dim, input_length=INP_LEN, mask_zero=True)(inputs)

 这是一个嵌入层,用于将输入的离散特征(SMILES 编码)映射到稠密的低维空间。

        x = tf.keras.layers.Conv1D(filters=NUM_FILTERS, kernel_size=3,  activation='relu', padding='valid',  strides=1)(x)
        x = tf.keras.layers.Conv1D(filters=NUM_FILTERS*2, kernel_size=3,  activation='relu', padding='valid',  strides=1)(x)
        x = tf.keras.layers.Conv1D(filters=NUM_FILTERS*3, kernel_size=3,  activation='relu', padding='valid',  strides=1)(x)

一系列卷积层,用于从输入序列中提取特征。

        x = tf.keras.layers.GlobalMaxPooling1D()(x)

全局最大池化层,用于从卷积层输出的特征图中提取最显著的特征。

        x = tf.keras.layers.Dense(1024, activation='relu')(x)
        x = tf.keras.layers.Dropout(0.1)(x)
        x = tf.keras.layers.Dense(1024, activation='relu')(x)
        x = tf.keras.layers.Dropout(0.1)(x)
        x = tf.keras.layers.Dense(512, activation='relu')(x)
        x = tf.keras.layers.Dropout(0.1)(x)

一系列全连接层,用于学习特征之间的非线性关系。

        outputs = tf.keras.layers.Dense(3, activation='sigmoid')(x)

模型的输出层,输出预测的概率。 

        model = tf.keras.models.Model(inputs=inputs, outputs=outputs)

 将输入和输出连接成模型。

        optimizer = tf.keras.optimizers.Adam(learning_rate=CFG.LR, weight_decay=CFG.WD)

 定义优化器,这里使用 Adam 优化器。

        loss = 'binary_crossentropy'

 定义损失函数,这里使用二元交叉熵损失函数。

        weighted_metrics = [tf.keras.metrics.AUC(curve='PR', name='avg_precision')]

 定义评估指标,这里使用了平均精度 (Average Precision)。

        model.compile(loss=loss, optimizer=optimizer, weighted_metrics=weighted_metrics)

 编译模型,将优化器、损失函数和评估指标配置到模型中。

        return model

 返回构建好的模型对象。

 8、交叉验证循环

FEATURES = [f'enc{i}' for i in range(142)]
TARGETS = ['bind1', 'bind2', 'bind3']
skf = StratifiedKFold(n_splits = CFG.NBR_FOLDS, shuffle = True, random_state = 42)
#定义了特征列的名称和目标列的名称,以及使用Stratified K-Fold进行交叉验证的划分。
all_preds = []
for fold,(train_idx, valid_idx) in enumerate(skf.split(train, train[TARGETS].sum(1))):
    
    if fold not in CFG.SELECTED_FOLDS:
        continue;
    
    X_train = train.loc[train_idx, FEATURES]
    y_train = train.loc[train_idx, TARGETS]
    X_val = train.loc[valid_idx, FEATURES]
    y_val = train.loc[valid_idx, TARGETS]

    es = tf.keras.callbacks.EarlyStopping(patience=5, monitor="val_loss", mode='min', verbose=1)
    checkpoint = tf.keras.callbacks.ModelCheckpoint(monitor='val_loss', filepath=f"model-{fold}.h5",
                                                        save_best_only=True, save_weights_only=True,
                                                    mode='min')
    reduce_lr_loss = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.05, patience=5, verbose=1)
    model = my_model()
    history = model.fit(
            X_train, y_train,
            validation_data=(X_val, y_val),
            epochs=CFG.EPOCHS,
            callbacks=[checkpoint, reduce_lr_loss, es],
            batch_size=CFG.BATCH_SIZE,
            verbose=1,
        )
    model.load_weights(f"model-{fold}.h5")
    oof = model.predict(X_val, batch_size = 2*CFG.BATCH_SIZE)
    print('fold :', fold, 'CV score =', APS(y_val, oof, average = 'micro'))
    
    preds = model.predict(test, batch_size = 2*CFG.BATCH_SIZE)
    all_preds.append(preds)

preds = np.mean(all_preds, 0)

这是交叉验证的主要循环。在每个折中,根据SELECTED_FOLDS选择指定的折数进行训练和预测。在每个折数中,首先选择对应的训练和验证数据,然后建立模型并进行训练。训练完成后,使用模型对测试集进行预测,并将预测结果存储在all_preds列表中。 

代码详解

定义特征和目标列

FEATURES = [f'enc{i}' for i in range(142)]
TARGETS = ['bind1', 'bind2', 'bind3']

 这里,FEATURES 是一个包含特征列名称的列表,TARGETS 是一个包含目标列名称的列表。

Stratified K-Fold 交叉验证:

skf = StratifiedKFold(n_splits=CFG.NBR_FOLDS, shuffle=True, random_state=42)

 这段代码初始化了一个 Stratified K-Fold 对象,使用了由 CFG.NBR_FOLDS 指定的折数。Stratified K-Fold 保持每个折中类别的分布,以确保每个折都代表整体数据集。

模型训练

all_preds = []
for fold, (train_idx, valid_idx) in enumerate(skf.split(train, train[TARGETS].sum(1))):
    if fold not in CFG.SELECTED_FOLDS:
        continue
    X_train = train.loc[train_idx, FEATURES]
    y_train = train.loc[train_idx, TARGETS]
    X_val = train.loc[valid_idx, FEATURES]
    y_val = train.loc[valid_idx, TARGETS]

 这个循环遍历了 Stratified K-Fold 对象生成的每个折。它将数据分割成当前折的训练集和验证集。

 回调函数

es = tf.keras.callbacks.EarlyStopping(patience=5, monitor="val_loss", mode='min', verbose=1)
checkpoint = tf.keras.callbacks.ModelCheckpoint(monitor='val_loss', filepath=f"model-{fold}.h5",
                                                    save_best_only=True, save_weights_only=True,
                                                    mode='min')
reduce_lr_loss = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.05, patience=5, verbose=1)

 这些是在模型训练过程中使用的回调函数。EarlyStopping 在监控的指标停止改善时停止训练。ModelCheckpoint 如果是到目前为止最佳的模型,则在每个周期后保存模型。ReduceLROnPlateau 在监控的指标停止改善时降低学习率。

模型训练(继续)

model = my_model()
history = model.fit(
    X_train, y_train,
    validation_data=(X_val, y_val),
    epochs=CFG.EPOCHS,
    callbacks=[checkpoint, reduce_lr_loss, es],
    batch_size=CFG.BATCH_SIZE,
    verbose=1,
)

 这段代码使用训练数据(X_trainy_train)训练模型。它在验证数据上(X_valy_val)验证模型的性能。回调函数用于监控训练过程。

加载最佳模型

model.load_weights(f"model-{fold}.h5")

 训练后,从 ModelCheckpoint 保存的文件中加载最佳模型的权重。

预测和评估

oof = model.predict(X_val, batch_size=2*CFG.BATCH_SIZE)
print('fold :', fold, 'CV score =', APS(y_val, oof, average='micro'))

 对验证集(X_val)进行预测。使用自定义函数 APS(此处未显示)评估模型的性能。

 最终预测

preds = np.mean(all_preds, 0)

 在所有折训练和验证完成后,对预测进行平均以获得测试数据的最终预测。这些预测存储在 preds 变量中。

9、生成预测结果

tst = pd.read_parquet('/kaggle/input/leash-BELKA/test.parquet')
tst['binds'] = 0
tst.loc[tst['protein_name']=='BRD4', 'binds'] = preds[(tst['protein_name']=='BRD4').values, 0]
tst.loc[tst['protein_name']=='HSA', 'binds'] = preds[(tst['protein_name']=='HSA').values, 1]
tst.loc[tst['protein_name']=='sEH', 'binds'] = preds[(tst['protein_name']=='sEH').values, 2]
tst[['id', 'binds']].to_csv('submission.csv', index = False)

原文地址:BELKA 1DCNN Starter with all data (kaggle.com)

  • 26
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值