赛题背景:
不同于只提供了时间上的度量传统的日历年龄,基于甲基化数据的生物学年龄能更好的应用于药物研发,疾病预防与治疗。因此基于甲基化数据预测生物学年龄很重要。
本体是属于回归问题。是机器学习用来预测一个连续变量的数值的问题。
代码理解:
配置环境:
本次实践是基于阿里天池的云端环境。能提供快速的算力
导入模块:
NumPy库用于进行对数组的计算与操作,是基本的库。
数据探索:
start_mem = df.memory_usage().sum() / 1024**2
计算初始内存以便于对内存优化分析。
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)
首先根据数据的范围对其的数据类型进行转换,以减小内存。
path = 'ai4bio'
traindata = pd.read_csv(f'{path}/traindata.csv', nrows=20000)
trainmap = pd.read_csv(f'{path}/trainmap.csv')
testdata = pd.read_csv(f'{path}/ai4bio_testset_final/testdata.csv', nrows=20000)
testmap = pd.read_csv(f'{path}/ai4bio_testset_final/testmap.csv')
根据内存选取适量的数据。
traindata = traindata.set_index('cpgsite')
traindata = traindata.T
traindata = traindata.reset_index()
traindata = traindata.rename(columns={'index':'sample_id'})
traindata.columns = ['sample_id'] + [i for i in range(20000)]
traindata.to_pickle(f'{path}/traindata.pkl')
因为原本数据为每一列代表一个样本,需要转置调整为每一行为一个样本;同时将index改为sample_id,并将标上特征id
pickle文件:是一种用于序列化(将对象转换成字节流)和反序列化(将字节流转换回对象)Python对象的文件格式。pickle模块允许我们将Python对象保存到磁盘上,并在需要时重新加载它们,同时保留对象的结构和数据。pickle文件通常用于存储和传输数据,特别是在机器学习和深度学习领域中,它常用于保存训练好的模型。
traindata = traindata.merge(trainmap[['sample_id', 'age', 'gender', 'sample_type', 'disease']],on='sample_id',how='left')
testdata = testdata.merge(testmap[['sample_id', 'gender']],on='sample_id',how='left')
将data数据与map数据合并,并添加新的列。
算出数据缺失率,并算出特征的相关性分数。
数据清洗:
数据清洗是指在数据分析和数据挖掘过程中,对原始数据进行筛选、转换和整理的过程。它的目的是去除无效或冗余的数据、修复缺失或错误的数据,并对数据进行统一格式的调整,以确保数据的质量和一致性。数据清洗可以包括去除重复数据、处理缺失值、纠正错误数据、转换数据类型、处理异常值等操作,以便为后续的数据分析和建模提供准确、可靠的数据基础。
disease_mapping = {
'control': 0,
"Alzheimer's disease": 1,
"Graves' disease": 2,
"Huntington's disease": 3,
"Parkinson's disease": 4,
'rheumatoid arthritis': 5,
'schizophrenia': 6,
"Sjogren's syndrome": 7,
'stroke': 8,
'type 2 diabetes': 9
}
sample_type_mapping = {'control': 0, 'disease tissue': 1}
gender_mapping = {'F': 0, 'M': 1}
traindata['disease'] = traindata['disease'].map(disease_mapping)
traindata['sample_type'] = traindata['sample_type'].map(sample_type_mapping)
traindata['gender'] = traindata['gender'].map(gender_mapping)
testdata['gender'] = testdata['gender'].map(gender_mapping)
用字典将疾病名称映射为对应的数值,以用于编码训练模型。
特征工程:
其目的是获取更好的训练数据特征。
traindata['max'] = traindata[[i for i in range(60000)]].max(axis=1)
traindata['min'] = traindata[[i for i in range(60000)]].min(axis=1)
traindata['std'] = traindata[[i for i in range(60000)]].std(axis=1)
traindata['var'] = traindata[[i for i in range(60000)]].var(axis=1)
traindata['skew'] = traindata[[i for i in range(60000)]].skew(axis=1)
traindata['mean'] = traindata[[i for i in range(60000)]].mean(axis=1)
traindata['median'] = traindata[[i for i in range(60000)]].median(axis=1)
testdata['max'] = testdata[[i for i in range(60000)]].max(axis=1)
testdata['min'] = testdata[[i for i in range(60000)]].min(axis=1)
testdata['std'] = testdata[[i for i in range(60000)]].std(axis=1)
testdata['var'] = testdata[[i for i in range(60000)]].var(axis=1)
testdata['skew'] = testdata[[i for i in range(60000)]].skew(axis=1)
testdata['mean'] = testdata[[i for i in range(60000)]].mean(axis=1)
testdata['median'] = testdata[[i for i in range(60000)]].median(axis=1)
构建了一些统计特征。
cols = [i for i in range(60000)] + ['gender','max','min','std','var','skew','mean','median']
创建特征。
本数据原始字段特征以连续型数值为主,可以按行进行聚合统计操作,如构建max、min、mean、std、skew等常见的数值统计特征。观察数据集的特征和分布情况。
最后选出合适的特征。
训练验证:
验证方法:K折交叉验证方法。k-fold交叉验证方法是一种常用的模型评估技术。它将数据集分成k个相等的子集,称为folds。然后,我们使用k-1个fold作为训练集来训练模型,并使用剩下的1个fold作为验证集来评估模型的性能。这个过程会k次重复,每次选择不同的验证集。 最后,我们将k次评估的结果综合起来,得到模型在整个数据集上的性能评估指标,通常是平均值或中位数。这种方法有助于减少过拟合风险,可以更客观地评估模型的性能。
学习率,控制模型参数更新的速度。值越大,模型更新越快,但可能陷入局部最优解;值越小,模型更新越慢,但可能收敛到更好的解。
树的深度,即决策树的最大层数。树的深度越深,模型的复杂度越高,可能导致过拟合;树的深度越浅,模型的复杂度越低,可能导致欠拟合。
特征优化:
PCA主成分析降维:原理:依据原来的特征分布图,得出最优拟合线,算出每个特征的差异值,差异值为每一个特征下每个样本距离中心的垂直距离,距离的平方和除上n-1,得出差异值最大的几个特征,将该特征下样本距离中心的垂直距离作为新的特征值。
LDA降维:主要思想:LDA算法的思想是将数据投影到低维空间之后,使得同一类数据尽可能的紧凑,不同类的数据尽可能分散。且使用于原始数据根据样本均值进行分类以及 不同类的数据拥有相同的协方差矩阵。特点:降维之后的维数最多为类别数-1。所以当数据维度很高,但是类别数少的时候,算法并不适用。
if testdata[col].nunique()==1: # 属性值唯一
drop_cols.append(col)
如果属性值唯一,那么对模型无用。
if testdata[col].isnull().sum() / test_df.shape[0] > 0.95: # 缺失率大于0.95
drop_cols.append(col)
如果缺失率大于0.95,那么删去该属性。
def correlation(data, threshold):
col_corr = []
corr_matrix = data.corr()
for i in range(len(corr_matrix)):
for j in range(i):
if abs(corr_matrix.iloc[i,j]) > threshold: # 相关性大于0.98
colname = corr_matrix.columns[i]
col_corr.append(colname)
return list(set(col_corr))
如果相关性大于0.98那么去掉其中一个属性。
模型创建:
def catboost_model(train_x, train_y, test_x, seed = 2023):
folds = 5
kf = KFold(n_splits=folds, shuffle=True, random_state=seed)
oof = np.zeros(train_x.shape[0])
test_predict = 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)))
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, 'bootstrap_type':'Bernoulli','random_seed':2023,
'od_type': 'Iter', 'od_wait': 100, 'random_seed': 11, 'allow_writing_files': False}
model = CatBoostRegressor(iterations=2000, **params)
model.fit(trn_x, trn_y, eval_set=(val_x, val_y),
metric_period=500,
use_best_model=True,
cat_features=[],
verbose=1)
val_pred = model.predict(val_x)
test_pred = model.predict(test_x)
oof[valid_index] = val_pred
test_predict += test_pred / kf.n_splits
score = mean_absolute_error(val_y, val_pred)
cv_scores.append(score)
print(cv_scores)
# 获取特征重要性打分,便于评估特征
if i == 0:
fea_ = model.feature_importances_
fea_name = model.feature_names_
fea_score = pd.DataFrame({'fea_name':fea_name, 'score':fea_})
fea_score = fea_score.sort_values('score', ascending=False)
fea_score.to_csv('feature_importances.csv', index=False)
return oof, test_predict
将数据集划分为5个折叠,然后创建KFold对象,用于交叉验证,然后创建一个大小与训练集样本数相同的零数组,用于保存交叉验证中的预测结果。创建一个大小与测试集样本数相同的零数组,用于保存最终预测结果。创建一个空列表,用于保存每个交叉验证折叠的得分。