特征工程是生成精确模型的最重要步骤之一。但是没有人喜欢它,因为这个步骤非常繁琐,我坚信任何繁琐的事情都可以自动化。虽然我的解决方案并没有完全消除对手工工作的需要,但它确实大大减少了手工工作,并产生了更好的结果。它还生成了一个在结构化数据集上始终优于梯度增强方法的模型。
本文将包含以下内容:
- 到底什么被自动化了?
- 它是如何工作的?
- 你如何建立它?
- 和其他型号相比怎么样?
工程特性
正确地做工程特性可能需要数周的EDA。但幸运的是,神经网络擅长的是寻找相互作用关系!
当向模型提供一组特征时,它必须了解哪些特征相互作用,以及它们是如何相互作用的。有了庞大的数据集,可以有无数的组合进行测试,这些模型往往专注于提供快速成功的交互。通过特性工程,您可以手动创建或组合特性,以确保模型正确的关注这些特征。
根据数据和问题的不同,有许多不同类型的特征工程方法。大多数可分为以下几类:
数据清理:有些人认为这个特征工程,其实并不是这样,他应该有在训练中有着自己独立的地位。总结来说,这一步的目的是特性工程成为可能之前需要确保数据是可用的。它涉及修复数据中的错误、处理缺失值、处理异常值、独热编码、扩展特性以及无数其他事情。在我看来,数据清理是唯一一个比特征设计更糟糕的步骤,所以任何能找到自动化这一步的方法的人都将成为我的新的发现。
均值编码:这一步涉及到将分类特征(如邮政编码)转换为模型可用的信息。例如,可以创建一个列,显示邮政编码的分组后的平均销售收入。
滞后变量:在数据中添加时间序列元素通常会有帮助。通过添加以前时期的价值,模型可以计算出随着时间的推移事情的趋势(例如上个月的销售额,前月的销售额,等等)。这个过程并不太复杂,可以通过简单的循环实现自动化。
交互:这个步骤涉及到以不同的方式组合特征。例如,你可以通过将广告驱动的客户购买除以广告的总点击次数来衡量在线广告的转化率。但如果转化率因产品价格的不同而变化很大呢?现在可以根据价格阈值创建单独的列。我们也很难发现并知道如何处理三阶(或更高阶)互动(例如,广告转化率因价格和类别而异)。到目前为止,这是特性工程中最微妙和最耗时的步骤。正确地做这个步骤需要做数周的EDA。幸运的是,神经网络擅长的是寻找相互作用!诀窍是确保模型确实寻找它们,这将是以后的重点。
概念
神经网络采用一组输入特征,并在它们之间创建相互作用,以最好地预测输出。如上所述,我们可以通过设计模型来强制模型考虑某些组合。但如果我们能迫使神经网络去考虑它们吗?如果我们可以确保神经网络以对目标输出产生最佳准确性的方式来设计这些特征的话,应该该怎么办?这里的关键就是是训练模型应以首先关注特征为主。
假设我们具有特征A,特征B,特征C和特征D,其目标输出为Y。此问题的第一步是创建一个预测每个特征的模型。我们为什么要关心预测特征?因为我们希望神经网络学习特定于每个特征的交互。
如果我们可以确保神经网络以对目标输出产生最佳精确的方式来设计这些功能,该怎么办?
确保特征网络以最终模型而不是单独的过程进行训练。这里的技巧是训练嵌入到每个特征层的嵌入层。对您来说是个好消息:经过几个月的努力,我得以开发出一种解决方案,并且它超出了我的期望。
代码
为了演示这些方法,我们将尝试预测COVID-19病人发生严重反应的可能性。您可以在此处找到“ Cleaned-Data.csv”数据集:https://www.kaggle.com/iamhungundji/covid19-symptoms-checker?select=Cleaned-Data.csv
让我们提取数据并创建训练,验证和测试数据集:
importpandasaspdimporttensorflowastffromsklearn.model_selectionimporttrain_test_splitfromtensorflowimportfeature_columnfromtensorflow.kerasimportlayersfromtensorflow.keras.callbacksimportModelCheckpointfromsklearn.metricsimportlog_lossX_train=pd.read_csv('covid_data.csv') y_train=X_train.pop('Severity_Severe').to_frame() X_train=X_train.iloc[:,:23] X_train, X_val, y_train, y_val=train_test_split( X_train, y_train,test_size=0.2,random_state=42) X_val, X_test, y_val, y_test=train_test_split( X_val, y_val,test_size=0.5,random_state=42)
现在,我们将要定义我们要为其创建特征模型的特征。由于我们没有很多特征,这里就全部使用它们(嵌入时将使用Country除外)。当模型包含数百个特征时,首先应该确定义最重要的特征,例如下面代码:
model_cols= ['Fever','Tiredness','Dry-Cough', 'Difficulty-in-Breathing', 'Sore-Throat','None_Sympton', 'Pains','Nasal-Congestion', 'Runny-Nose','Diarrhea', 'None_Experiencing','Age_0-9', 'Age_10-19','Age_20-24','Age_25-59', 'Age_60_','Gender_Female','Gender_Male', 'Gender_Transgender','Contact_Dont-Know', 'Contact_No','Contact_Yes']
这些特征中的每一个都会与我们试图预测的目标目标(Severity_Severe)一起成为整体模型的不同辅助输出。在创建TensorFlow数据集时,我们还必须将它们定义为输出特征。请注意,我们通过在末尾添加“ _out”来重命名每个特征,以免TensorFlow被重复的名称弄糊涂。请注意,我们还为目标输出添加了一个额外的“ _aux_out”列。这样一来,我们就可以围绕目标特征训练一个单独的特征模型,该模型也将输入到最终模型中。这是一个称为跳过连接的过程,该过程使模型可以学习围绕同一特征集的深层和浅层交互。
Y_train_df=X_train[model_cols].copy() Y_train_df.columns=Y_train_df.columns+"_out"Y_train_df['Severity_Severe_out'] =y_train['Severity_Severe'] Y_train_df['Severity_Severe_aux_out'] =y_train['Severity_Severe'] trainset=tf.data.Dataset.from_tensor_slices(( dict(X_train),dict(Y_train_df))).batch(256) Y_val_df=X_val[model_cols].copy() Y_val_df.columns=Y_val_df.columns+"_out"Y_val_df['Severity_Severe_out'] =y_val['Severity_Severe'] Y_val_df['Severity_Severe_aux_out'] =y_val['Severity_Severe'] valset=tf.data.Dataset.from_tensor_slices(( dict(X_val),dict(Y_val_df))).batch(256) Y_test_df=X_test[model_cols].copy() Y_test_df.columns=Y_test_df.columns+"_out"Y_test_df['Severity_Severe_out'] =y_test['Severity_Severe'] Y_val_df['Severity_Severe_aux_out'] =y_val['Severity_Severe'] testset=tf.data.Dataset.from_tensor_slices(( dict(X_test),dict(Y_test_df))).batch(256)
我们要创建的第一个函数是add_model。我们将向此函数提供特征名称,定义层的数量和大小,表示是否要使用批量归一化,定义模型的名称并选择输出激活。hidden_layers变量将为每个层有一个单独的列表,第一个数字是神经元的数量,第二个数字是dropout比例。该函数的输出将是输出层和最终的隐藏层(特征工程),这些层将作为最终模型的基础。使用诸如hyperopt之类的工具时,此功能允许轻松进行超参数调整。
defadd_model( feature_outputs=None,hidden_layers=[[512,0],[64,0]], batch_norm=False,model_name=None,activation='sigmoid'): ifbatch_norm==True: layer=layers.BatchNormalization()(feature_outputs) else: layer=feature_outputsforiinrange(len(hidden_layers)): layer=layers.Dense(hidden_layers[i][0], activation='relu', name=model_name+'_L'+str(i))(layer) last_layer=layerifbatch_norm==True: layer=layers.BatchNormalization()(layer) ifhidden_layers[i][1] >0: layer=layers.Dropout(hidden_layers[i][1])(layer) output_layer=layers.Dense(1, activation=activation, name=model_name+'_out')(layer) returnlast_layer, output_layer