使用自编码器可以保持预测能力的同时进行数据匿名化数据。
在这篇文章中,我们将看到如何使用自动编码器(一种特殊的人工神经网络)来匿名化数据。该方法所提取的数据的潜在表示可以在不影响原始数据性能的前提下用于下游的机器学习预测任务中。
本教程分为两个部分。在第一个例子中,我将展示一个自动编码器的结构。在第二部分中,我将展示如何使用自动编码器对表格数据进行编码,以匿名化数据,并将其用于其他机器学习任务,同时保护隐私。
Autoencoder
自动编码器是一种特殊的神经网络,由编码器和解码器两部分组成。编码器部分接收输入数据并将其转换为潜表示,而解码部分试图从潜表示中重构输入数据。损失是输入数据和重构数据之间的距离。
一个受过良好训练的自动编码器能够提供一个良好的潜在表示。这种表示与原始数据有很大的不同,但它拥有包含在输入层中的所有信息。
为了说明这一点,让我们尝试在众所周知的公共数据集MNIST上运行一个自动编码器。
让我们为本教程导入一些包。
frompandasimportread_csv, set_option, get_dummies, DataFramefromsklearn.preprocessingimportMinMaxScalerfromsklearn.model_selectionimporttrain_test_splitfromsklearn.ensembleimportRandomForestClassifierfromsklearn.model_selectionimportcross_validatefromsklearn.inspectionimportpermutation_importancefromnumpyimportmean, max, prod, array, hstackfromnumpy.randomimportchoicefrommatplotlib.pyplotimportbarh, yticks, ylabel, xlabel, title, show, scatter, cm, figure, imshowfromtensorflow.keras.layersimportInput, Dense, Dropout, Activation, BatchNormalizationfromtensorflow.kerasimportModelfromtensorflow.keras.datasetsimportmnistfromtensorflow.keras.callbacksimportEarlyStoppingfromtensorflow.keras.utilsimportplot_modelfromtqdmimporttqdm
我们将构建和训练不同的自动编码器,因此,让我们定义一个函数。
defbuild_autoencoder(dim_input, dim_layer_1, dim_layer_2): input_layer=Input(shape=(dim_input,)) x=Activation("relu")(input_layer) x=Dense(dim_layer_1)(x) x=Activation("relu")(x) bottleneck_layer=Dense(dim_layer_2)(x) x=Activation("relu")(bottleneck_layer) x=Dense(dim_layer_1)(x) x=Activation("relu")(x) output_layer=Dense(dim_input, activation='relu')(x) encoder=Model(input_layer, bottleneck_layer) autoencoder=Model(input_layer, output_layer) autoencoder.compile(optimizer='adam', loss='mse') returnautoencoder, encoder
上面定义的自动编码器有三个隐藏层。输入层和输出层具有相同的大小。当我们训练神经网络时,计算输入和输出的差值来反向传播损失和更新权值,而在预测阶段,我们只使用编码器部分的权值,因为我们只需要潜表示。
现在,加载已经分割成训练集和测试集的数据集。
(X_train, y_train), (X_test, y_test) =mnist.load_data() X_train=X_train.astype('float32') /255.X_test=X_test.astype('float32') /255.X_train=X_train.reshape((len(X_train), prod(X_train.shape[1:]))) X_test=X_test.reshape((len(X_test), prod(X_test.shape[1:])))
构建我们的自动编码器。
autoencoder, encoder=build_autoencoder( dim_input=X_train.shape[1], dim_layer_1=64, dim_layer_2=16)
这个模型的结构如下:
我们用训练集来训练网络的权值。
callbacks= [ EarlyStopping( monitor='val_loss', min_delta=0.0001, patience=5, restore_best_weights=True ) ]autoencoder.fit( X_train, X_train, epochs=100, batch_size=256, shuffle=True, validation_split=0.3, callbacks=callbacks)
一旦训练结束,我们就可以在测试集上测试自动编码器。
encoded=array(encoder(X_test)) decoded=array(autoencoder(X_test))
让我们绘制原始数据、编码的表示和重构的数据。
fig=figure(figsize=(16, 8))n_plots=10n_rows=int(n_plots/2)forjinrange(n_plots): fig.add_subplot(n_rows, 6, 3*j+1) plot_tmp=imshow(X_test[j].reshape([28, 28])) plot_tmp.axes.get_xaxis().set_visible(False) plot_tmp.axes.get_yaxis().set_visible(False) fig.add_subplot(n_rows, 6, 3*j+2) plot_tmp=imshow(encoded[j].reshape([1, 16])) plot_tmp.axes.get_xaxis().set_visible(False) plot_tmp.axes.get_yaxis().set_visible(False) fig.add_subplot(n_rows, 6, 3*j+3) plot_tmp=imshow(decoded[j].reshape([28, 28])) plot_tmp.axes.get_xaxis().set_visible(False) plot_tmp.axes.get_yaxis().set_visible(False) show()
正如你所看到的,重构的图像(来自潜在的表现)与输入的非常相似。这意味着由自动编码器学习的瓶颈表示(编码)是原始数据的良好表示,即使它不能被人理解。
我们将在一个表格数据集上重用这个想法,通过在潜在空间中得到它的表示来匿名化原始数据。
数据集
在这个实验中,我们将使用银行营销数据集。该数据是关于一家葡萄牙银行机构的直接电话营销活动(https://archive.ics.uci.edu/ml/datasets/Bank+Marketing)。机器学习的任务是分类,我们需要建立一个模型来预测客户是否会订阅定期存款。
我在这里介绍这个数据集中的变量的描述。你可以在网站上找到他们。
我们加载数据集并执行非常简单的处理。我删除了列“duration”,因为在执行调用之前它是未知的。
df=read_csv("data/bank-additional-full.csv", sep=";") y= (df.y=="yes") +0deldf["duration"] deldf["y"] X=get_dummies(df) array_columns=X.columnsmin_max_scaler=MinMaxScaler() X=min_max_scaler.fit_transform(X)
数据集概述:
基于原始数据的基准性能
在匿名化数据之前,我们可以尝试使用一个基本的随机森林进行交叉验证,以评估基线性能。需要指出的是,我们并不是要在这里找到最好的模型,我们关心的是在原始数据上训练的模型和在编码(匿名)数据上训练的模型之间的差异。
rf=RandomForestClassifier( n_estimators=500, max_depth=2, n_jobs=8, random_state=42) dict_performance=cross_validate( estimator=rf, X=X, y=y, cv=10, n_jobs=4, return_train_score=True, scoring=[ "balanced_accuracy", "f1_weighted", "roc_auc", "average_precision" ] ) df_performance=DataFrame( {"ORIGINAL": [mean(dict_performance[k]) \forkindict_performance.keys()]}, index=dict_performance.keys() )
我们还可以绘制特征的重要性,以了解哪个特征影响目标变量。
rf=RandomForestClassifier( n_estimators=500, max_depth=2, n_jobs=8, random_state=42) rf.fit(X, y) fi=permutation_importance( estimator=rf, X=X, y=y, n_repeats=10, n_jobs=8, random_state=42).importances_meanfigure(figsize=(16,8)) barh( y=range(10, 0, -1), width=sorted(fi, reverse=True)[:10], alpha=0.9) ylabel("Feature") yticks( range(10, 0, -1), array_columns[fi.argsort()[::-1][:10]] ) xlabel("Importance") title("Original features importance") show()
正如你所注意到的,重要的特征大多是与以前的竞选结果和总体经济情况有关的。