DeepChem 数据变换器(Transformer)完全指南:从数据规约到模型输入的标准化管线
【免费下载链接】deepchemDemocratizing Deep-Learning for Drug Discovery, Quantum Chemistry, Materials Science and Biology项目地址: https://gitcode.com/GitHub_Trending/de/deepchem
dc.trans是 DeepChem 中与Dataset、Model并列的三大核心构建块之一。本指南以官方 API 文档 transformers.rst 为骨架,结合 transformers.py、duplicate.py 源码及 trans/tests 测试用例,系统讲解所有内置 Transformer 的数学原理、构造参数、可逆性与适用场景,帮助你为真实世界的"野性"数据建立一条可控、可复现、可逆的数据预处理管线。
读完本文,你将能够:为回归任务选择正确的标签归一化方案;为不均衡分类数据自动平衡样本权重;理解哪些变换可以逆推回原始物理单位、哪些不可逆;以及如何用undo_transforms在评估阶段把模型输出还原成有物理意义的数值。
为什么需要 Transformer:给数据"戴上笼头"
机器学习系统通常非常"娇气"——它们希望输入输出落在预先指定的范围内,或服从某种干净的数学分布。而真实数据是狂野且难以控制的。DeepChem 的解决方案就是dc.trans.Transformer对象:一个把源数据集变得更适合学习的数学操作。例如:
- 某个 transformer 可以归一化特征,使它们具有零均值与单位标准差;
- 另一个可以截断超出给定范围的值;
- 还有一个可以充当数据增强例程,从每个源数据点生成多张不同的图像(变换不必是"一对一"的)。
从源码 docstring(transformers.py 中Transformer类注释)可以看到三个关键设计原则:
- 可链式组合:数据管线往往需要串联多个变换,Transformer 被设计成可以链式应用;
- 可扩展:可以作用在大型
dc.data.Dataset对象上; - 注意线程安全:Transformer 通常不是线程安全的,处理超大数据集时需要小心。
在init.py 中,所有 Transformer 被统一导出,便于from deepchem import trans后直接使用dc.trans.NormalizationTransformer这类快捷导入。
Transformer 基类:抽象父类与四个变换开关
dc.trans.Transformer是所有变换器的抽象父类(源码位置)。它不应该被直接实例化——直接初始化会抛出ValueError:
if self.__class__.__name__ == "Transformer": raise ValueError( "Transformer is an abstract superclass and cannot be directly instantiated. " "You probably want to instantiate a concrete subclass instead." )但它提供了若干有用的方法实现,是所有子类共享的骨架:
构造参数:选择变换对象
| 参数 | 类型/默认值 | 含义 |
|---|---|---|
transform_X | bool, 默认False | 是否变换特征 X |
transform_y | bool, 默认False | 是否变换标签 y |
transform_w | bool, 默认False | 是否变换样本权重 w |
transform_ids | bool, 默认False | 是否变换样本标识 ids |
dataset | dc.data.Dataset,默认None | 用于计算统计量的数据集 |
构造时有一个硬性断言:assert transform_X or transform_y or transform_w or transform_ids——至少有一个变换开关必须为True,否则无法通过初始化。
核心方法
transform_array(X, y, w, ids):对一组(X, y, w, ids)数组执行变换,返回变换后的四元组。这是一个抽象方法,每个子类必须自己实现(否则抛出NotImplementedError)。untransform(transformed):逆转已存储的变换。注意:并非所有变换都是一对一的,因此untransform不一定总是被定义(例如ClippingTransformer就明确抛NotImplementedError)。transform(dataset, parallel=False, out_dir=None, **kwargs):对整个数据集应用变换,内部调用Dataset.transform方法。关键细节:- 若
out_dir被指定而dataset不是DiskDataset,会先通过DiskDataset.from_numpy把内存数据写盘; - 当
transform_y=True但数据集的y_shape == ()(没有标签)时会抛出ValueError: Cannot transform y when y_values are not present,transform_w同理——这一点在测试 test_normalization.py 中被专门验证:对无标签数据集变换 y 或 w 都会触发ValueError,而变换 X 是合法的。 parallel=True时用多进程并行变换,适合大数据集加速。
- 若
transform_on_array(...):旧接口,已标记 DEPRECATED,官方建议改用transform_array,并警告将在 DeepChem 3.0 移除(FutureWarning)。
全局逆变换工具undo_transforms
undo_transforms 是评估阶段的"时间机器":
def undo_transforms(y, transformers): y_out = np.asarray(y) for transformer in reversed(transformers): if transformer.transform_y: y_out = transformer.untransform(y_out) return y_out它按逆序逐一调用untransform:如果transformers = [t1, t2],则会先执行t2.untransform再执行t1.untransform。也就是说,变换按给定顺序应用,逆变换按相反顺序撤销。这是恢复原始物理单位(如溶解度、能量值)的标准入口。
另外仓库中还保留了两个已标记 DEPRECATED 的梯度相关工具:undo_grad_transforms与get_grad_statistics(用于力/能量统计),官方明确警告"DO NOT USE",建议手动实现力计算变换,文章不再展开。
通用 Transformer 详解(General Transformers)
这一组是日常管线中使用频率最高的变换器,全部位于 transformers.py。
NormalizationTransformer:零均值单位标准差
将数据变换为零均值、单位标准差(Z-score 标准化)。构造参数:
| 参数 | 类型/默认值 | 含义 |
|---|---|---|
transform_X | bool, 默认False | 是否变换 X |
transform_y | bool, 默认False | 是否变换 y |
transform_w | bool, 默认False | 是否变换 w |
dataset | Dataset, 默认None | 用于计算均值/标准差的训练集 |
transform_gradients | bool, 默认False | 是否同步变换梯度(依赖已废弃的get_grad_statistics) |
move_mean | bool, 默认True | 为True时执行(x - mean) / std;为False时仅执行x / std(不移均值) |
实现要点(源码):
- 约束:
transform_X与transform_y不能同时为True;且不支持 w 变换(抛ValueError)。 - 均值/标准差通过
dataset.get_statistics(X_stats=True, y_stats=False)或对应 y 版本计算; - 病态情况防护:当某个任务的方差为 0 时,标准差被替换为 1(
y_stds_np[y_stds_np == 0] = 1.),避免除零; - 变换时使用
np.nan_to_num处理 NaN/Inf; untransform通过z * std + mean恢复(move_mean=False时仅z * std),并专门处理了单任务y_stds是标量的情况以及维度广播问题,防止把任务维错当成 batch 维。
典型用法(来自源码 docstring 示例):
import deepchem as dc import numpy as np n_samples, n_features, n_tasks = 10, 3, 1 ids = np.arange(n_samples) X = np.random.rand(n_samples, n_features) y = np.random.rand(n_samples, n_tasks) w = np.ones((n_samples, n_tasks)) dataset = dc.data.NumpyDataset(X, y, w, ids) transformer = dc.trans.NormalizationTransformer(transform_y=True, dataset=dataset) dataset = transformer.transform(dataset)测试 test_normalization.py 验证了核心不变量:y 变换后mean ≈ 0、std ≈ 1,X 与 w 保持不变,ids 保持不变,且untransform(y_t) ≈ y(逆变换误差在atol=1e-7内);对 X 变换时,仅当原始标准差非零的特征才要求变换后标准差为 1。
MinMaxTransformer:线性缩放到 [0, 1]
通过每列的 min/max 把每个轴(除第一个外)线性缩放到 0 到 1 之间。数学形式(源码 docstring):
A_t = np.nan_to_num((A - A_min) / (A_max - A_min))构造参数:transform_X、transform_y、dataset。约束与 NormalizationTransformer 一致:X 和 y 只能二选一(同设为True会抛ValueError: Can only transform only one of X and y),不支持 w。
实现细节(源码):
- 用
np.min(dataset.X, axis=0)/np.max计算每列极值,并断言len(y_min) == dataset.y.shape[1](多任务维度一致性); - 除零防护:分母
(max - min) > 0的地方保留差值,否则替换为 1(np.where+np.ones_like),再配合np.nan_to_num; untransform通过z * (max - min) + min恢复,同样处理了任务维度的expand_dims广播问题;- 在多任务学习中,该变换可以保证每个任务获得同等的重要性(各任务数值范围一致)。
用法示例(源码 docstring):
transformer = dc.trans.MinMaxTransformer(transform_y=True, dataset=dataset) dataset = transformer.transform(dataset)ClippingTransformer:截断极端值
把超出范围的绝对值直接截断(阈值裁剪),用于抑制异常值/离群点。构造参数:
| 参数 | 类型/默认值 | 含义 |
|---|---|---|
transform_X | bool, 默认False | 是否变换 X |
transform_y | bool, 默认False | 是否变换 y |
dataset | Dataset, 默认None | 数据集 |
x_max | float, 默认5. | X 允许的最大绝对值 |
y_max | float, 默认500. | y 允许的最大绝对值 |
实现(源码):X 中大于x_max的置为x_max,小于-x_max的置为-x_max;y 同理用y_max。可以同时变换 X 和 y,但同样不支持 w(设置transform_w会抛ValueError)。需要注意:该变换不可逆——untransform直接抛出NotImplementedError("Cannot untransform datasets with ClippingTransformer."),因为截断丢失了信息。
LogTransformer:对数变换
计算A = np.log(A + 1)(以自然对数为底,加 1 保证非负输入安全)。构造参数:
| 参数 | 类型/默认值 | 含义 |
|---|---|---|
transform_X | bool, 默认False | 是否变换 X |
transform_y | bool, 默认False | 是否变换 y |
features | list[int], 默认None | 仅变换这些特征下标;None表示全部 |
tasks | list[str], 默认None | 仅变换这些任务;None表示全部 |
dataset | Dataset, 默认None | 数据集 |
实现(源码)支持局部变换:当features/tasks非空时,只有列出的特征/任务被取对数,其余保持原值。untransform为np.exp(z) - 1(同样只对指定列恢复)。X 和 y 只能二选一。
适合处理呈长尾/数量级跨度的标签(如某些活性指标),把乘法关系转化为加法关系,便于线性模型拟合。
CDFTransformer:累积分布函数(秩次)变换
对数据排序并分配基于直方图等级的值,作用类似于计算数据集的累积分布函数:把连续分布样本替换为其对应的 CDF 值,从而把任意分布"拉直"为近似均匀分布。构造参数:
| 参数 | 类型/默认值 | 含义 |
|---|---|---|
transform_X | bool, 默认False | 是否变换 X |
transform_y | bool, 默认False | 是否变换 y |
dataset | Dataset, 默认None | 数据集(transform_y=True时必填,否则抛ValueError) |
bins | int, 默认2 | 计算直方图使用的分箱数 |
核心辅助函数get_cdf_values(array, bins)(源码)的实现逻辑:
- 把 1D 数组重塑为
(n_rows, 1); - 按行划分
parts = n_rows / bins个区间,奇偶分箱数采用不同的归一化分母(奇数用bins - 1,偶数用bins),把每行映射到一个[0, 1]区间内的等级值; - 对每一列独立做
np.argsort,把等级值按原数据顺序回填,得到"秩次均匀化"后的数组。
untransform仅对 y 有意义,且实现很特殊:直接返回训练时的原始self.y(相当于按查找表还原),对 X 则抛NotImplementedError。用法示例:
X = np.random.normal(size=(N, n_feat)) y = np.random.normal(size=(N,)) dataset = dc.data.NumpyDataset(X, y) cdftrans = dc.trans.CDFTransformer(transform_y=True, dataset=dataset, bins=n_bins) dataset = cdftrans.transform(dataset)PowerTransformer:幂次特征扩展
按给定的幂次向量计算数据的幂变换,用于构造高阶特征(x_i^2、x_i^3等)。构造参数:
| 参数 | 类型/默认值 | 含义 |
|---|---|---|
transform_X | bool, 默认False | 是否变换 X |
transform_y | bool, 默认False | 是否变换 y |
dataset | Dataset, 默认None | 该参数在实现中被忽略(不需要统计量) |
powers | list[int], 默认[1] | 要计算的特征/标签幂次列表 |
实现(源码):第一个幂次的结果作为首列,其余幂次通过np.hstack横向拼接,因此特征维度会乘以len(powers)。对 y 做幂次扩展时要求 y 必须是一维或(N, 1)形状,否则抛ValueError("This transform is not defined for multitask y")。untransform取z[:, :orig_len](只保留第一块)再开1/powers[0]次方。
示例:powers = [1, 2, 0.5]表示同时构造恒等、平方与平方根三组特征。
BalancingTransformer:按类平衡样本权重
针对分类任务中的类别不均衡,重算每个样本的权重 w,使所有类别的样本权重之和相等。构造参数只有dataset。
实现(源码):
- 只变换 w(
transform_w=True),X、y、ids 均不变; - 对每个任务独立计算:先剔除权重为 0 的样本(多任务数据集中常见"部分样本只有部分任务有标签"),再对每个类别统计
num_c,类别权重为N_task / num_c(分子分母相乘恰好得到N_task,从而保证各类权重和一致);某个类完全缺失时权重记为 0; - 二分类与多分类(
y取有限个离散值)均适用; - 要求 y、w 形状为
(N,)或(N, n_tasks),否则抛ValueError。
测试 test_balancing.py 验证了二分类、单任务、五任务多任务、五分类等场景下sum(w[y==0]) == sum(w[y==1])(多分类时任意两个类别的权重和相等)这一核心不变量,且零权重样本在变换后仍保持零权重;transform(dataset, out_dir=tmpdirname)可以把结果写到磁盘(test_transform_to_directory)。
transformer = dc.trans.BalancingTransformer(dataset=dataset) dataset = transformer.transform(dataset)DuplicateBalancingTransformer:复制少数类样本
与 BalancingTransformer 不同,它通过实际复制少数类样本而非仅增大权重来平衡数据(duplicate.py)。对于数值上脆弱、无法承受不均衡样本权重的模型更友好。
- 同时变换
X、y、w、ids(transform_X/y/w/ids全为True); - 仅支持单任务数据集:
len(dataset.get_task_names()) > 1时抛ValueError; - 复制倍率
duplication_ratio = int(weight_largest / c_weight),即把每个类都复制到与最大权重类相近的数量(受整数舍入影响,允许近似相等); - 同样要求 y/w 形状合法。
transformer = dc.trans.DuplicateBalancingTransformer(dataset=dataset) dataset = transformer.transform(dataset)ImageTransformer:图像尺寸缩放
把图像数组缩放到指定宽高(源码)。支持形状(n_samples, width, height)与(n_samples, width, height, channels)的图像(以及同样形状的 mask 标签)。
| 参数 | 类型/默认值 | 含义 |
|---|---|---|
size | tuple[int, int] | 目标尺寸(width, height) |
transform_X | bool, 默认True | 是否变换 X |
transform_y | bool, 默认False | 是否变换 y(如同时缩放 mask) |
依赖 Pillow:未安装时抛ImportError。实现细节:PIL 只接受 uint8 输入,因此先(img * 255).astype(np.uint8)再resize,最后除以 255 以最小化缩放过程的信息损失;对 X 和 y 的输入形状分别有assert校验。文档示例把(10, 256, 256, 3)的图像与 mask 同时缩放到(128, 128),验证resized_X.shape == (10, 128, 128, 3)。
img_transform = dc.trans.ImageTransformer(size=(128, 128), transform_X=True, transform_y=True) resized_dataset = dataset.transform(img_transform)FeaturizationTransformer:对数据集批量特征化
在已有数据集上运行一个Featurizer来重算 X(源码),适合"先加载原始数据、再补做特征化"的场景。构造参数:dataset与featurizer(必填,None时抛ValueError)。transform_array内部即X = self.featurizer.featurize(X)。
trans = dc.trans.FeaturizationTransformer(dataset, dc.feat.CircularFingerprint()) dataset = trans.transform(dataset)FlatteningTransformer:碎片展开(原子贡献解释专用)
该变换是原子贡献模型解释(atom-based model interpretation)的预处理步骤:把per_atom_fragmentation=True特征化出的分子碎片数据集展开为预测所需的扁平数据集,使每个碎片继承父分子的 id / y / w,随后即可用"整分子预测 − 碎片预测"的方式计算每个原子的贡献(源码,引文为 Polishchuk 等 2016 年 JCIM 论文)。transform_array通过np.repeat(ids, [len(i) for i in X])让每个碎片携带父分子标签,再把嵌套的碎片数组np.array([j for i in X for j in i])展平。
特定用途 Transformer(Specified Usecase Transformers)
这一组服务于特定的模型或数据形态,通常作为模型的fit_transformers或训练前的强制预处理步骤。
CoulombFitTransformer:库仑矩阵随机化与二值化
在训练MultitaskFitTransformRegressor时对库仑矩阵(Coulomb Matrix)特征批次执行随机化(realize)与二值化(binarize)操作(源码)。其流水线为X_transform = normalize(expand(realize(X))):
realize:对每个分子的库仑矩阵按范数降序加入高斯噪声np.random.normal(0, noise)后重排行列(打破对称等价性),再取上三角元素展开为向量;expand:对每个特征维度按步长step=1.0在[0, max_i]区间上生成np.tanh((X - k)/step)二值化特征(训练阶段用 10 轮随机化估计每列max);normalize:(X - mean) / std标准化;- 不可逆:
untransform抛NotImplementedError。
源码 docstring 示例中,n_features=3的库仑矩阵输入经过变换后model.n_features == 12,直观展示了维度扩展。
fit_transformers = [dc.trans.CoulombFitTransformer(dataset)] model = dc.models.MultitaskFitTransformRegressor( n_tasks, [n_features, n_features], batch_size=n_samples, fit_transformers=fit_transformers, n_evals=1)IRVTransformer:ECFP 到 IRV 特征
把 ECFP 指纹变换为 IRV(Input Relevance Vector,K 近邻)特征,是MultitaskIRVClassifier训练前的必选预处理(源码)。构造参数:
| 参数 | 类型 | 含义 |
|---|---|---|
K | int | 使用的近邻个数 |
n_tasks | int | 任务数 |
dataset | Dataset | 训练集(作为相似性参考集) |
原理:X_transform计算目标集与参考集逐样本的 Jaccard 相似度#(1 in intersection) / #(1 in union),对每个任务用realize找出相似度最高的 K 个近邻,输出每个样本2*K*n_tasks维特征(K 个相似度值 + K 个对应标签)。实现上通过分片矩阵乘法matrix_mul(X1, X2, shard_size=5000)支持大矩阵,transform中按 5000 行一批处理,可输出NumpyDataset或写盘的DiskDataset。注意:该变换不可逆,且依赖 TensorFlow 环境。对应测试见 test_IRV.py。
trans = dc.trans.IRVTransformer(K=10, n_tasks=n_tasks, dataset=dataset) dataset = trans.transform(dataset)DAGTransformer:ConvMol 邻接表到 DAG 计算顺序
供DAGModel(有向无环图模型)训练前使用:把ConvMol特征转换为 DAG 计算顺序。核心是把一个 n 原子分子展开为 n 个 DAG,每个 DAG 以不同原子为根(源码)。构造参数max_atoms: int = 50(允许的最大原子数)。
核心算法UG_to_DAG:从目标原子出发做广度优先径向传播,记录依赖边(current_atom, atom_adj),再逆序构造父子关系,按"父少者优先"排序后以max_atoms填充/截断,最终为每个原子生成一个max_atoms × max_atoms的父矩阵。代码注释还处理了分子存在多个连通片时无法覆盖全部原子的 break 分支。不可逆。
trans = dc.trans.DAGTransformer(max_atoms=5) dataset = trans.transform(dataset)对应测试 test_DAG.py。
RxnSplitTransformer:化学反应 SMILES 源/目标切分
为机器翻译类任务(如 USPTO 反应预测)把reactant>reagent>product形式的反应 SMILES 拆成 source 串与 target 串(源码):
sep_reagent=True(默认):source 为reactant>reagent,target 为product;sep_reagent=False(混合训练模式):试剂并入反应物一侧,source 变为reactant.reagent>(当 reagent 为空时保持reactant>)。
transform_array通过字符串split('>')切分并np.column_stack输出(N, 2)的 X。只变换特征字段,y/w/ids 原样返回;不可逆。源码 docstring 给出了两类模式的完整输入输出示例,测试见 test_rxn_transform.py。
from deepchem.trans.transformers import RxnSplitTransformer trans = RxnSplitTransformer(sep_reagent=True) split_reactions = trans.transform_array(X=reactions, y=np.array([]), w=np.array([]), ids=np.array([]))图像增强工具箱:DataTransforms
除 Transformer 之外,transformers.py 还提供了一个面向图像数据增强的工具类DataTransforms(依赖 PIL/scipy),涵盖十余种操作,可在数据加载后直接调用:
| 方法 | 说明 |
|---|---|
scale(h, w) | 缩放图像 |
flip(direction="lr"/"ud") | 左右/上下翻转 |
rotate(angle) | 按角度旋转(scipy.ndimage) |
gaussian_blur(sigma=0.2) | 高斯模糊 |
center_crop(x_crop, y_crop) | 中心裁剪(水平/垂直各移除指定像素数) |
crop(left, top, right, bottom) | 自定义矩形裁剪 |
convert2gray() | 转灰度(Y'UV 亮度系数[0.2989, 0.5870, 0.1140]) |
shift(width, height, mode, order) | 平移(支持 constant/nearest/reflect/wrap 边界模式,样条插值阶数 0-5) |
gaussian_noise(mean, std) | 高斯噪声 |
salt_pepper_noise(prob, salt, pepper) | 椒盐噪声 |
median_filter(size) | 中值滤波 |
实战建议:如何组合一套数据管线
结合上述 API,一个典型的回归/分类流程可以按以下模式组织(参考资料:transformers.rst 与 trans 源码):
- 回归任务:对标签用
NormalizationTransformer(transform_y=True, dataset=train_dataset)或MinMaxTransformer归一化;训练完在测试集上预测后,用undo_transforms(y_pred, [transformer])恢复原始量纲再计算指标; - 分类不均衡:用
BalancingTransformer(dataset=train_dataset)(改权重)或DuplicateBalancingTransformer(复制样本,仅单任务)在训练前平衡数据; - 离群值/长尾:先用
ClippingTransformer截断极端标签,再叠加LogTransformer压缩量级; - 特征扩展:
PowerTransformer(powers=[1, 2, 0.5])构造非线性特征;FeaturizationTransformer可对已有数据集批量补做指纹/描述符特征化; - 模型专属预处理:
IRVTransformer、DAGTransformer、CoulombFitTransformer、RxnSplitTransformer分别对应 IRV 分类器、DAG 图模型、库仑矩阵回归器与反应翻译任务的强制前置步骤,按各自模型文档使用; - 图像任务:
ImageTransformer统一输入尺寸,DataTransforms做增强; - 可逆性核对:只有
NormalizationTransformer、MinMaxTransformer、LogTransformer(指定列)和CDFTransformer(y,按查找表)提供有意义的untransform;ClippingTransformer、CoulombFitTransformer、IRVTransformer、DAGTransformer、RxnSplitTransformer均不可逆,需要在预处理前确认评估指标是否需要原始量纲。
所有 Transformer 的统一入口、便捷导入与全局逆变换工具集中在 deepchem/trans/init.py,完整的单测覆盖位于 deepchem/trans/tests(含 normalization、minmax、clipping、log、cdf、power、balancing、duplicate、image、featurization、flattening、IRV、DAG、coulomb、rxn、data_transforms 等独立测试文件),可作为理解每个变换行为边界的活文档。
【免费下载链接】deepchemDemocratizing Deep-Learning for Drug Discovery, Quantum Chemistry, Materials Science and Biology项目地址: https://gitcode.com/GitHub_Trending/de/deepchem
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考