大数跨境

导师:“数据有 0,你还直接 np.log(x)?”我:“代码报错了……”导师:“别只复制 Codex 代码,log1p 和 expm1 你真会用吗?”

导师:“数据有 0,你还直接 np.log(x)?”我:“代码报错了……”导师:“别只复制 Codex 代码,log1p 和 expm1 你真会用吗?” 机器学习和人工智能AI
2026-11-14
0

哈喽,大家好 ~

大家在整理数据的过程中,总是会发现为什么收入、房价、点击量这些数据,总有几个特别大的值?

其实很容易理解,因为现实数据经常不是均匀增长的。极少数大值会拉长数据尾巴,让模型更难学习。

今儿咱们来聊一种非常实用的数据变换方法:对数变换。你可以把它理解成一种“压缩大数、拉开小数”的处理方式,让数据更适合进入线性模型、神经网络和各种机器学习算法。

理解对数变换

假设有两组数据:

  • 10 增长到 20,增加了 10;
  • 1000 增长到 1010,同样增加了 10。

从绝对数来看,它们增加得一样多。但在很多业务场景中,10 到 20 是翻倍,变化非常明显;1000 到 1010 却几乎没有变化。

对数变换关注的不是“增加了多少”,而是“增长了多少倍”。

最常见的形式是:

其中,  是原始数据,  是变换后的数据。对数的底数可以是  、10 或 2,机器学习中最常用自然对数。

即:原始数据相差很大,对数变换后会被压缩到更接近的范围。

解决什么问题?

很多真实数据都有右偏特征,例如用户收入、商品销量、网站访问量和房屋价格。大部分样本数值不大,但少量样本特别大。

这会带来两个问题:

第一,模型容易被极端大值牵着走。第二,数据的增长关系可能不是线性的,而是“越到后面增长越慢”。

例如房屋面积从 50 平方米增加到 100 平方米,影响可能很明显;从 500 平方米增加到 550 平方米,影响就没那么大。

如果特征中存在 0,可以使用:

代码中对应 np.log1p(x)。预测结果需要还原时,再使用:

这里的 1 是为了避免   没有定义。

完整案例

下面我们构造一组“房屋面积”和“房价”的数据。房屋面积服从偏斜分布,面积越大,房价增长越慢。

然后分别使用原始面积和对数面积训练线性回归模型。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_squared_error

np.random.seed(42)

# 偏斜的房屋面积,少数房子面积特别大
area = np.random.exponential(scale=80, size=500) + 20
price = 80 + 35 * np.log1p(area) + np.random.normal(0, 12, 500)

x_train, x_test, y_train, y_test = train_test_split(
    area.reshape(-1, 1), price, test_size=0.25, random_state=42
)

model_raw = LinearRegression().fit(x_train, y_train)

x_log = np.log1p(area).reshape(-1, 1)
xl_train, xl_test, yl_train, yl_test = train_test_split(
    x_log, price, test_size=0.25, random_state=42
)
model_log = LinearRegression().fit(xl_train, yl_train)

pred_raw = model_raw.predict(x_test)
pred_log = model_log.predict(xl_test)

print("原始面积 R2:", round(r2_score(y_test, pred_raw), 3))
print("对数面积 R2:", round(r2_score(yl_test, pred_log), 3))

fig, axes = plt.subplots(1, 2, figsize=(13, 5))

# 图1:原始特征与对数特征的拟合效果
idx = np.argsort(area)
axes[0].scatter(area, price, c=price, cmap="turbo",
                alpha=0.7, edgecolors="white", linewidths=0.4)
axes[0].plot(area[idx], model_raw.predict(area[idx].reshape(-1, 1)),
             color="#ff1744", linewidth=3, label="raw fit")
axes[0].plot(area[idx], model_log.predict(x_log[idx]),
             color="#00c853", linewidth=3, label="log fit")
axes[0].set_title("Feature transformation comparison")
axes[0].set_xlabel("House area")
axes[0].set_ylabel("Price")
axes[0].legend()

# 图2:预测值与残差,观察模型在哪些区域出错
raw_res = y_test - pred_raw
log_res = yl_test - pred_log

axes[1].scatter(pred_raw, raw_res, c=x_test.ravel(),
                cmap="plasma", alpha=0.65, label="raw residual")
axes[1].scatter(pred_log, log_res, c="#00b0ff",
                alpha=0.45, label="log residual")
axes[1].axhline(0, color="black", linestyle="--")
axes[1].set_title("Residual analysis")
axes[1].set_xlabel("Predicted price")
axes[1].set_ylabel("Residual")
axes[1].legend()

plt.tight_layout()
plt.show()
原始面积 R2: 0.621
对数面积 R2: 0.81

第一张图中,蓝紫到黄色代表房价逐渐升高。红线是直接使用面积的线性拟合,绿线是使用对数面积后的拟合。由于真实关系本身接近“对数增长”,绿线通常会更贴近散点。

第二张图是残差分析。横轴是预测房价,纵轴是残差。好的模型会让点云围绕 0 随机分布,而且不同预测区间的波动比较接近。如果原始模型在大房屋区域出现明显偏离,而对数模型的残差更均匀,就说明对数变换缓解了极端值带来的影响。

注意点

对数变换不是所有数据都适合。

如果数据全部大于 0,可以使用 np.log(x);如果包含 0,优先使用 np.log1p(x)。如果包含负数,不能直接取对数,需要先结合业务含义做平移、拆分正负值,或者选择其他变换方法。

另外,对数变换改变了数据尺度,所以模型预测完成后,要记得使用 np.expm1() 还原结果。训练集和测试集也必须使用同一种变换,不能只转换其中一部分。

最后

对数变换的核心作用,是压缩极端大值、减弱右偏,让原本弯曲的增长关系更接近线性。它特别适合收入、销量、访问量、房价等“少数值特别大”的数据。

大家后面还可以继续尝试两个方向:一是比较 log1p 和 PowerTransformer 的效果;二是在随机森林、XGBoost 和神经网络中分别测试变换前后的模型表现。记住,最终不要只看  ,还要结合残差图判断模型是否真的学得更稳定。

干货

最近准备了16大块的内容,124个算法问题总结,完整的机器学习小册,免费领取~

图片
领取:备注「算法小册」即可~
图片
扫码如有问题,记得添加微信号:xiaobai_ml012

【声明】内容源于网络
0
0
机器学习和人工智能AI
让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
内容 456
粉丝 0
机器学习和人工智能AI 让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
总阅读10.8k
粉丝0
内容456