Datawhale机器学习夏令营task3

#ai夏令营#Datawhale

简单说一下自己实现的思路和对代码的注解

导包

导入Python中用于数据分析、机器学习建模和化学信息学所需的库。以下是每个库的简要说明:

  • numpy: 提供强大的多维数组对象和相应的操作。

  • pandas: 提供高性能、易用的数据结构和数据分析工具。

  • catboost: 一个用于机器学习的库,特别是分类和回归任务。

  • sklearn.model_selection: 包含模型选择的多种方法,如交叉验证。

  • sklearn.metrics: 包含评估模型性能的多种指标。

  • rdkit: 一个化学信息学和机器学习软件,用于处理化学结构。

  • sklearn.feature_extraction.text: 提供将文本转换为特征向量的Tf-idf向量化器。

  • tqdm: 用于在长循环中添加进度条的库。

  • sys: 与Python解释器密切相关的模块,提供访问由解释器使用或维护的变量和函数。

  • os: 提供与操作系统交互的功能。

  • gc: 垃圾收集器接口,用于手动标记对象为可删除。

  • re: 正则表达式库,用于字符串搜索和替换。

  • argparse: 用于编写用户友好的命令行接口。

  • warnings: 用于发出警告的库,这里用来忽略警告信息。

import numpy as np
import pandas as pd
from catboost import CatBoostClassifier
from sklearn.model_selection import StratifiedKFold, KFold, GroupKFold
from sklearn.metrics import f1_score
from rdkit import Chem
from rdkit.Chem import Descriptors
from sklearn.feature_extraction.text import TfidfVectorizer
import tqdm, sys, os, gc, re, argparse, warnings
warnings.filterwarnings('ignore')
  1. 从指定路径加载训练集和测试集数据。
  2. 为了一致性,从训练集中移除了存在于训练集但不在测试集中的列(即 'DC50 (nM)' 和 'Dmax (%)')。
  3. 确定了在测试集中存在大量缺失值(非空值少于10个)的列并将其列名存储在一个列表中。
  4. 然后从训练集和测试集中删除了这些包含大量空值的列,以保证数据质量和一致性。
  5. 将清洗后的训练集和测试集合并,形成一个新的DataFrame,便于统一进行后续的数据处理。
  6. 提取了合并后DataFrame中的特定列名,这些列名用于特征工程。
# 导入 pandas 包以处理 Excel 数据。
import pandas as pd

# 从 './dataset-new/traindata-new.xlsx' 路径加载训练集数据。
train = pd.read_excel('./dataset-new/traindata-new.xlsx')

# 从 './dataset-new/testdata-new.xlsx' 路径加载测试集数据。
test = pd.read_excel('./dataset-new/testdata-new.xlsx')

# 从训练集中移除 'DC50 (nM)' 和 'Dmax (%)' 这两列,因为它们在测试数据中不存在,且可能不需要用于预测模型。
train = train.drop(['DC50 (nM)', 'Dmax (%)'], axis=1)

# 定义一个空列表 drop_cols,用于存储在测试数据集中非空值少于10个的列名。
drop_cols = []
for f in test.columns:
    # 检查每列的非空值数量,如果少于10个,则将该列名添加到 drop_cols 列表中。
    if test[f].notnull().sum() < 10:
        drop_cols.append(f)

# 使用 drop 方法从训练集和测试集中删除 drop_cols 列表中的列,
# 以避免使用这些包含大量缺失值的列进行后续的分析或建模。
train = train.drop(drop_cols, axis=1)
test = test.drop(drop_cols, axis=1)

# 使用 pd.concat 将清洗后的训练集和测试集合并成一个名为 data 的 DataFrame,
# 设置 axis=0 以按行合并(垂直堆叠),ignore_index=True 以重置合并后的索引。
# 这样做是为了便于进行统一的特征工程处理。
data = pd.concat([train, test], axis=0, ignore_index=True)

# 从合并后的 DataFrame 中提取从第三列开始的所有列名存储到变量 cols 中。
# 这里假设前两列是不需要参与特征工程的列,例如 ID 或标签列。
cols = data.columns[2:]
数据预处理

清洗数据并对齐特征列,为后续的特征工程和模型训练做好准备。

  1. 对齐数据集

    • 删除训练数据集中的DC50 (nM)Dmax (%)列,因为测试数据集中不包含这两列。
  2. 删除缺失值较多的列

    • 遍历测试数据集的每一列,如果某列非空值的数量少于10个,则将该列名添加到drop_cols列表中。
    • 从训练集和测试集中删除drop_cols列表中的列,移除这些包含大量缺失值的列,以避免后续分析或建模中的问题。
  3. 合并数据集

    • 将清洗后的训练集和测试集合并成一个新的DataFrame——data,便于统一进行后续的特征工程处理。
  4. 提取特征列

    • 获取data中的所有列名,除去前两列,以提取特征列用于后续的机器学习模型训练和测试。
# 首先导入pandas库,并从Excel文件中读取数据集。
import pandas as pd

# 读取训练数据集和测试数据集
train = pd.read_excel('./dataset-new/traindata-new.xlsx')
test = pd.read_excel('./dataset-new/testdata-new.xlsx')

# 因为测试数据集不包含DC50 (nM)和Dmax (%)列,所以在训练数据集中也删除这两列,以对齐。
train = train.drop(['DC50 (nM)', 'Dmax (%)'], axis=1)

# 定义一个空列表`drop_cols`,用于存储测试数据集中非空值(未缺失值)小于10个的列名。
drop_cols = []
for f in test.columns:
    if test[f].notnull().sum() < 10:
        drop_cols.append(f)

# 从训练集和测试集中删除在`drop_cols`中的列,以避免在后续分析或建模中使用这些包含大量缺失值的列。
train = train.drop(drop_cols, axis=1)
test = test.drop(drop_cols, axis=1)

# 使用`pd.concat`方法将清洗后的训练集和测试集合并成一个名为`data`的DataFrame,便于进行统一的特征工程处理。
data = pd.concat([train, test], axis=0, ignore_index=True)

# 获取所有列名,除了前两列(一般前两列可能是ID列或其它非特征列),用于下一步的特征工程处理
cols = data.columns[2:]
特征工程

特征工程指的是把原始数据转变为模型训练数据的过程,目的是获取更好的训练数据特征。特征工程能使得模型的性能得到提升,有时甚至在简单的模型上也能取得不错的效果。

补充一些特征工程的知识

特征工程是数据科学和机器学习中一个关键步骤,它涉及将原始数据转化为可用于机器学习模型的有效特征。良好的特征工程可以显著提升模型的性能。特征工程的步骤和方法有很多,具体包括但不限于以下几个方面:

  1. 数据清洗

    • 删除或填补缺失值。
    • 处理异常值,例如将异常值转化为合理的数值或删除。
    • 数据标准化和归一化,将数据缩放到统一的范围。
  2. 特征选择

    • 选取对模型预测有重要影响的特征,删除与目标无关或多余的特征。
    • 使用统计方法(如相关性分析、PCA等)或模型(如Lasso回归)进行特征选择。
  3. 特征构造

    • 通过对原始数据进行变换来创建新的特征,比如时间特征的提取(年、月、日、时等),文本特征的提取(词频、情感分析等)。
    • 设计算术组合特征(如对某些列进行加减乘除运算)。
  4. 编码分类变量

    • 将分类变量转化为数值形式,例如使用独热编码(One-Hot Encoding)或标签编码(Label Encoding)。
  5. 创建多维特征

    • 使用特征交叉(Feature Cross),比如交叉特征来捕获两个或多个特征之间的关联。
  6. 特征降维

    • 使用技术如主成分分析(PCA)或线性判别分析(LDA)来减少特征数量,同时保持重要的信息。

具体的例子

假设我们有一个包含以下列的原始数据集:

  • Date:日期
  • Temperature:当天的温度
  • Weather:天气状况(如晴天、雨天)
  • Sales:销售量

通过特征工程,我们可以:

  1. 时间特征提取:从Date列提取年、月、星期等特征。
  2. 填补缺失值:如果Temperature有缺失值,可以用平均值、中位数或前后值填充。
  3. 编码分类变量:将Weather列进行独热编码,变成晴天、雨天等独立列。
  4. 构造交叉特征:创建组合特征,比如‘Weather * Temperature’来捕捉天气与温度对销售的共同影响。
  5. 数据归一化:将Temperature数据缩放到0-1之间,方便模型处理。

特征工程虽然通常需要较多的领域知识和技巧,但其效果是显著的。对数据进行恰当的特征工程不仅能提升模型的准确性,还能加快模型的训练速度和提高模型的可解释性。

# 导入所需的库
from rdkit import Chem
from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd

# 将SMILES转换为分子对象列表,然后再转换回SMILES字符串列表
# 使用RDKit的Chem模块将SMILES字符串转化为分子对象,再将对象转回SMILES字符串,并放入列表中。
data['smiles_list'] = data['Smiles'].apply(lambda x: [Chem.MolToSmiles(mol, 
isomericSmiles=True) for mol in [Chem.MolFromSmiles(x)]])
# 将列表中的SMILES字符串用空格连接成一个字符串
data['smiles_list'] = data['smiles_list'].map(lambda x: ' '.join(x))

# 初始化TfidfVectorizer并计算TF-IDF值
# max_df=0.9表示当某个词语出现的文档数占总文档数的比例超过0.9时,将其视为无关紧要的词。
# min_df=1表示至少要在一个文档中出现的词才能纳入计算。
tfidf = TfidfVectorizer(max_df=0.9, min_df=1, sublinear_tf=True)
# 使用TF-IDF算法对SMILES字符串列表进行向量化,并转换为数值矩阵
res = tfidf.fit_transform(data['smiles_list'])

# 将结果转换为DataFrame格式,列名以smiles_tfidf_为前缀
tfidf_df = pd.DataFrame(res.toarray())
tfidf_df.columns = [f'smiles_tfidf_{i}' for i in range(tfidf_df.shape[1])]

# 将TF-IDF结果DataFrame按列合并到原始data数据集
data = pd.concat([data, tfidf_df], axis=1)

# 定义一个自然数编码的函数,用于将分类变量转换为数值
# unique函数返回series中唯一值的列表,然后map函数通过字典实现了分类变量到自然数的映射。
def label_encode(series):
    unique = list(series.unique())
    return series.map(dict(zip(unique, range(series.nunique()))))

# 对所有列进行编码,如果数据类型是object(即字符串),则进行自然数编码
for col in cols:
    if data[col].dtype == 'object':
        data[col] = label_encode(data[col])

# 通过判断Label列是否为空来分割训练集和测试集,并重置索引
train = data[data.Label.notnull()].reset_index(drop=True)
test = data[data.Label.isnull()].reset_index(drop=True)

# 选择特征列,排除uuid、Label和smiles_list列
# 使用列表推导来选择特征列,将其存储在features列表中
features = [f for f in train.columns if f not in ['uuid', 'Label', 'smiles_list']]

# 构建训练集和测试集,选择特征列
x_train = train[features]
x_test = test[features]

# 提取训练集标签,并将其转换为整数类型
y_train = train['Label'].astype(int)
  1. SMILES字符串处理

    • 将SMILES字符串转换为分子对象,再转换为SMILES字符串列表,并用空格连接。
    • 目的是将化学分子式标准化,方便后续处理。
  2. TF-IDF计算

    • 使用TfidfVectorizer计算TF-IDF值,转换SMILES字符串列表为数值向量。
    • 这样通过TF-IDF向量可以捕捉到不同化学分子的特征信息。
  3. 结果转换和合并

    • 将TF-IDF矩阵转换为DataFrame,并与原始数据集合并,扩展特征维度。
  4. 自然数编码

    • 编写函数实现分类变量的自然数编码,将字符串类型的分类值转换为数值。
    • 遍历数据集的各个列,对字符串类型的列进行自然数编码。
  5. 数据集分割

    • 根据Label列是否为空,将数据集分割为训练集和测试集,并重置索引。
    • 训练集用于模型训练,测试集用于模型预测和评估。
  6. 特征选择

    • 选择特征列,排除uuid、Label和smiles_list列,将其存储在features列表中。
    • 确保训练集和测试集使用相同的特征列。
  7. 训练数据准备

    • 构建训练集和测试集的特征数据。
    • 提取训练集的标签数据并转换为整数类型。

模型训练与预测

# 导入所需的库
import numpy as np
import pandas as pd
from sklearn.model_selection import KFold
from sklearn.metrics import f1_score
from catboost import CatBoostClassifier

# 定义交叉验证模型函数
def cv_model(clf, train_x, train_y, test_x, clf_name, seed=2022):
    
    # 使用KFold进行交叉验证,设置5折,shuffle=True表示每轮打乱数据,random_state设定随机种子
    kf = KFold(n_splits=5, shuffle=True, random_state=seed)

    # 初始化训练数据集的预测结果数组
    train = np.zeros(train_x.shape[0])
    # 初始化测试数据集的预测结果数组
    test = np.zeros(test_x.shape[0])

    # 用于存储每一折验证集的F1分数
    cv_scores = []
    
    # KFold分层抽样,生成训练集和验证集索引
    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 = train_x.iloc[train_index], train_y[train_index]
        val_x, val_y = train_x.iloc[valid_index], train_y[valid_index]
               
        # 定义模型参数
        params = {
            'learning_rate': 0.1,
            'depth': 6,
            'l2_leaf_reg': 10,
            'bootstrap_type':'Bernoulli',
            'random_seed':seed,
            'od_type': 'Iter',
            'od_wait': 100,
            'allow_writing_files': False,
            'task_type':'CPU'
        }

        # 初始化CatBoost分类器
        model = clf(iterations=20000, **params, eval_metric='AUC')

        # 训练模型
        model.fit(trn_x, trn_y, 
                  eval_set=(val_x, val_y), # 验证集
                  metric_period=100,       # 每100次迭代打印一次日志
                  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数组中对应的索引位置
        train[valid_index] = val_pred
        # 将测试集预测结果累加除以折数,目的是取平均值
        test += test_pred / kf.n_splits

        # 计算当前折的F1分数,阈值设为0.5
        cv_scores.append(f1_score(val_y, np.where(val_pred>0.5, 1, 0)))
        
        print(cv_scores)
    
    # 输出所有折的F1分数、平均分数和标准差
    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

# 调用cv_model函数,对CatBoost分类器进行交叉验证训练
cat_train, cat_test = cv_model(CatBoostClassifier, x_train, y_train, x_test, "cat")

# 将测试集预测结果转换为DataFrame并保存为CSV文件
pd.DataFrame(
    {
        'uuid': test['uuid'],
        'Label': np.where(cat_test>0.5, 1, 0)
    }
).to_csv('submit.csv', index=None)
  1. 交叉验证模型函数 cv_model

    • 输入参数

      • clf: 选择的分类器(如CatBoostClassifier)。
      • train_x: 训练集特征数据。
      • train_y: 训练集标签数据。
      • test_x: 测试集特征数据。
      • clf_name: 分类器名称,用于打印日志。
      • seed: 随机种子,用于确保结果的可重复性。
    • 初始化

      • 使用KFold进行数据分割,设定5折交叉验证,并初始化预测结果数组和保存每一折验证分数的列表。
    • 交叉验证循环

      • 对每一折执行:
        • 分割训练集和验证集。
        • 定义模型参数并初始化分类器。
        • 训练模型,并在验证集上进行验证。
        • 记录验证集的预测结果并累加测试集预测结果。
        • 计算验证集的F1分数并存储。
    • 输出结果

      • 输出每一折的F1分数、平均分数和标准差。
      • 返回训练集和测试集的预测结果。
  2. 调用 cv_model 函数

    • 使用CatBoostClassifier作为分类器,进行交叉验证训练和预测。
  3. 保存预测结果

    • 将测试集的预测结果转换为DataFrame,并保存为CSV文件,包含uuid和预测的Label

后续的补充内容还没看,后面再更新吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值