AI分类:特征不够用了之后

如果只能用一句话说AI分类:先把失败复现出来。

上个月有个需求:要对用户行为数据进行分类,判断用户是活跃用户、普通用户还是流失用户。

问题的由来

上个月有个需求:要对用户行为数据进行分类,判断用户是活跃用户、普通用户还是流失用户。数据已经存在数仓里,字段也不少,但真正要拿来训练时发现事情没那么简单。

原始数据大概是这个样子的:

SELECT
    user_id,
    last_login_time,
    total_sessions,
    avg_session_duration,
    feature_usage_count,
    subscription_status
FROM user_behavior_summary
WHERE data_date >= '2026-06-01'

乍一看数据挺齐全,但实际拿来做分类时遇到了几个明显的问题:

  1. 特征之间的尺度差异巨大:avg_session_duration 以分钟为单位,可能达到几百,而 feature_usage_count 通常只有个位数
  2. 缺失值处理:部分用户没有 subscription_status 记录
  3. 标签定义不明确:活跃、普通、流失的边界在哪里?连续 7 天不登录算流失,还是 30 天?

这些问题不解决,模型再花哨也不会有好的效果。

从数据到特征的转换

特征预处理

拿到原始数据后,第一件事就是做标准化。这里我用了 StandardScaler,把所有特征都缩放到均值为 0、标准差为 1 的范围内。

from sklearn.preprocessing import StandardScaler

# 提取特征列
feature_columns = ['total_sessions', 'avg_session_duration', 'feature_usage_count']
X = data[feature_columns].values

# 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

这里有个小坑:训练集和测试集要用同一个 scaler,否则测试集的分布会被错误估计。我曾经犯过这样的错误,在训练集上拟合 scaler,然后对整个数据集做标准化,导致模型在测试集上的表现异常差。

处理缺失值

对于缺失值,我试过两种方式:

  1. 直接删除:如果缺失值比例小于 5%,可以考虑直接删除
  2. 填充:用均值、中位数或最频繁值填充

最终选择了均值填充,因为在这个场景下,缺失值本身可能就代表了某种信息(比如新用户可能没有完整的历史数据)。

from sklearn.impute import SimpleImputer

imputer = SimpleImputer(strategy='mean')
X_imputed = imputer.fit_transform(X_scaled)

标签工程

最难的一步其实是标签的定义。经过几次尝试,最终是这样划分的:

  • 活跃用户:过去 7 天内有登录,且 session 数量 > 5
  • 普通用户:过去 14 天内有登录,但 session 数量 <= 5
  • 流失用户:过去 14 天内无登录

这样划分后,数据分布大概是:活跃 30%,普通 50%,流失 20%。还算平衡,不至于严重失衡。

模型选择与训练

尝试的模型

我试了几种常见的分类模型,分别记录了它们的表现:

from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neural_network import MLPClassifier

models = {
    'Logistic Regression': LogisticRegression(max_iter=1000),
    'SVM': SVC(kernel='rbf'),
    'Random Forest': RandomForestClassifier(n_estimators=100),
    'MLP': MLPClassifier(hidden_layer_sizes=(64, 32), max_iter=500)
}

实际测试下来,随机森林在这个数据集上的表现最好,准确率达到了 87%,而且训练速度也很快。逻辑回归的表现也不错,达到 83%,但在处理非线性关系时明显不如随机森林。

训练流程

整个训练流程可以概括为下面这个图:

flowchart TD A[原始数据] --> B[数据清洗<br/>处理缺失值] B --> C[特征标准化] C --> D[训练集/测试集分割] D --> E[模型训练] E --> F[模型评估] F --> G{是否满足要求?} G -->|否| H[调整参数/特征工程] H --> C G -->|是| I[模型部署]

关键点在于:每次调整参数或特征后,都要回到数据标准化的环节,确保训练和测试的一致性。

踩坑记录

坑一:数据泄露

第一次训练时,我犯了一个严重错误:直接对整个数据集做标准化,然后再分割训练集和测试集。这导致测试集的信息泄露到了训练集中,模型在测试集上的准确率虚高。

正确做法是:先分割,再分别做标准化。

from sklearn.model_selection import train_test_split

# 先分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 再标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

坑二:过拟合

随机森林很容易过拟合,特别是在树的深度很大、树的数量很多的时候。我一开始设置 max_depth=None,结果训练集准确率 95%,但测试集只有 75%。

后来限制了树的深度,使用了更多的验证集做监控,过拟合问题才缓解:

model = RandomForestClassifier(
    n_estimators=100,
    max_depth=10,
    min_samples_split=5,
    min_samples_leaf=2,
    random_state=42
)

坑三:类别不平衡

虽然标签分布还算平衡,但在某些时间段,流失用户的比例明显偏低,导致模型对流失用户的预测能力很差。

解决方案:

  1. 使用 class_weight='balanced' 参数
  2. 采用过采样或欠采样技术
  3. 使用更适合不平衡数据的评估指标(如 F1-score)

评估与结果

评估指标

准确率在分类问题里不够用,尤其是在类别不平衡的情况下。我还看了这几个指标:

  • 精确率:预测为某类的用户中,真正属于该类的比例
  • 召回率:真正属于某类的用户中,被正确预测的比例
  • F1-score:精确率和召回率的调和平均
from sklearn.metrics import classification_report, confusion_matrix

print(classification_report(y_test, y_pred))
print(confusion_matrix(y_test, y_pred))

最终结果

最终模型的表现:

  • 整体准确率:87%
  • 活跃用户 F1-score:0.85
  • 普通用户 F1-score:0.88
  • 流失用户 F1-score:0.82

流失用户的识别仍然是最难的,这也很合理:流失用户的数据本来就少,且特征不够明显。

实际应用

把模型部署到线上后,又遇到一个新问题:实时性要求。用户行为更新很快,但模型训练需要时间。

最后的解决方案是:

  1. 模型每天凌晨重新训练一次
  2. 对于需要实时预测的场景,使用上一天的模型
  3. 对于特别重要的业务,采用增量学习的方式更新模型

一些心得

做完这个项目后,对分类问题有了一些新的认识:

  1. 数据质量远比模型重要:垃圾数据再花哨的模型也拯救不了
  2. 特征工程是核心:好的特征能让简单模型也能有不错的表现
  3. 评估指标要贴合业务:不是所有场景都适合用准确率
  4. 平衡和取舍:模型复杂度、训练时间、预测准确率之间需要权衡

分类问题看似简单,但真正落地时会遇到各种边界情况。与其追求完美的模型,不如先把数据质量搞好,特征工程做扎实,再考虑模型选择。

写到这里,突然想起一句话:所有机器学习问题,最后都会变成数据问题。这句话一点都不假。


如果你也在做用户行为分类,或者在特征工程方面有更好的实践,欢迎交流。

版权声明: 本文首发于 指尖魔法屋-AI分类:特征不够用了之后https://blog.thinkmoon.cn/post/320-ai-classification-feature-category-guide/) 转载或引用必须申明原指尖魔法屋来源及源地址!