小Cの已经记不起来的博客

机器学习入门:数据脱敏 数据预处理

起因

前阵子接了个小活儿,要拿一批真实用户数据训练个分类模型,结果打开 csv 一看,姓名、手机号、身份证号全在里面。直接拿去训练肯定不行,万一哪天数据泄露出去,麻烦就大了。以前总觉得数据脱敏是安全团队该操心的事,真轮到自己头上才发现,这活儿不提前想好,等数据已经散出去了再补救,基本就是灾难现场。

顺便把预处理也一起捋一遍,毕竟脱敏完的数据也不能直接喂模型,中间还有一堆脏活累活要干。

数据脱敏:先别急着训练

脱敏的核心思路就一句话:让数据没法对应到具体的人,但尽量保留对模型有用的统计特征。常见的几种做法:

我用得最多的是 Faker 这个库,生成假数据是真的方便:

from faker import Faker
import pandas as pd

fake = Faker('zh_CN')
df = pd.read_csv('raw_data.csv')

df['name'] = [fake.name() for _ in range(len(df))]
df['phone'] = [fake.phone_number() for _ in range(len(df))]

跑完之后每行都换成了假名假号,但性别、年龄段这些特征还在,模型该学的东西不受影响。不然你就慢慢一列一列手动改吧,几千行数据改到天荒地老。

有个坑要注意:如果是拿哈希做脱敏,同一列里相同的原始值必须映射成相同的哈希值,别每行都重新加盐,不然“同一用户出现多次”这个信息就直接丢了。

数据预处理:脏活累活都在这里

脱敏完的数据直接喂模型基本是不行的。我第一次跑的时候报了一堆 NaN 的错,回去扫了一眼才发现,数据比想象中脏多了。

缺失值,先用 df.isnull().sum() 看一眼比例。缺得少的列我一般用中位数或者众数填,缺得太多的就直接删了,硬填进去反而引入噪声:

df['age'].fillna(df['age'].median(), inplace=True)
df = df.drop(columns=['fax_number'])  # 缺了92%,留着没意义

标准化,数值特征量纲差太多必须处理,不然梯度下降会被量纲大的特征带偏。用 sklearn 的 StandardScaler 就行:

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df[['income', 'age']] = scaler.fit_transform(df[['income', 'age']])

这里有个经典错误:fit_transform 只能在训练集上做,测试集要用同一个 scaler 去 transform。我一开始图省事对全量数据 fit 了一遍,等反应过来这算数据泄露的时候,之前的实验结果全部作废,又重跑了一遍...

类别变量编码,性别这种类别少的直接 one-hot;城市这种几百个取值的,one-hot 出来维度爆炸,可以用 target encoding 或者按频次编码,视情况选。

顺序很重要

把上面这些串起来,正确的流程是:脱敏 → 处理缺失值 → 划分训练集和测试集 → 在训练集上 fit scaler、算编码映射,再应用到测试集。我一开始把划分放在最后,结果测试集的信息提前渗进了训练过程,指标虚高得离谱,一度以为自己调参天赋异禀,后来才发现是流程错了...

写在最后

整个流程说起来就四步,但每一步都有能踩的坑。我的建议是先把整条 pipeline 写成脚本跑通,再回头慢慢调,不然每改一步都手动敲一遍,太折磨了。数据这块儿功夫下够了,后面调模型的时候真的能省不少事,至少不用半夜对着 NaN 报错发呆了。

评论

还没有评论。

发表评论

提交后评论将经过自动审核,审核通过后公开展示。

未在播放