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'
乍一看数据挺齐全,但实际拿来做分类时遇到了几个明显的问题:
- 特征之间的尺度差异巨大:
avg_session_duration以分钟为单位,可能达到几百,而feature_usage_count通常只有个位数 - 缺失值处理:部分用户没有
subscription_status记录 - 标签定义不明确:活跃、普通、流失的边界在哪里?连续 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,然后对整个数据集做标准化,导致模型在测试集上的表现异常差。
处理缺失值
对于缺失值,我试过两种方式:
- 直接删除:如果缺失值比例小于 5%,可以考虑直接删除
- 填充:用均值、中位数或最频繁值填充
最终选择了均值填充,因为在这个场景下,缺失值本身可能就代表了某种信息(比如新用户可能没有完整的历史数据)。
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%,但在处理非线性关系时明显不如随机森林。
训练流程
整个训练流程可以概括为下面这个图:
关键点在于:每次调整参数或特征后,都要回到数据标准化的环节,确保训练和测试的一致性。
踩坑记录
坑一:数据泄露
第一次训练时,我犯了一个严重错误:直接对整个数据集做标准化,然后再分割训练集和测试集。这导致测试集的信息泄露到了训练集中,模型在测试集上的准确率虚高。
正确做法是:先分割,再分别做标准化。
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
)
坑三:类别不平衡
虽然标签分布还算平衡,但在某些时间段,流失用户的比例明显偏低,导致模型对流失用户的预测能力很差。
解决方案:
- 使用
class_weight='balanced'参数 - 采用过采样或欠采样技术
- 使用更适合不平衡数据的评估指标(如 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
流失用户的识别仍然是最难的,这也很合理:流失用户的数据本来就少,且特征不够明显。
实际应用
把模型部署到线上后,又遇到一个新问题:实时性要求。用户行为更新很快,但模型训练需要时间。
最后的解决方案是:
- 模型每天凌晨重新训练一次
- 对于需要实时预测的场景,使用上一天的模型
- 对于特别重要的业务,采用增量学习的方式更新模型
一些心得
做完这个项目后,对分类问题有了一些新的认识:
- 数据质量远比模型重要:垃圾数据再花哨的模型也拯救不了
- 特征工程是核心:好的特征能让简单模型也能有不错的表现
- 评估指标要贴合业务:不是所有场景都适合用准确率
- 平衡和取舍:模型复杂度、训练时间、预测准确率之间需要权衡
分类问题看似简单,但真正落地时会遇到各种边界情况。与其追求完美的模型,不如先把数据质量搞好,特征工程做扎实,再考虑模型选择。
写到这里,突然想起一句话:所有机器学习问题,最后都会变成数据问题。这句话一点都不假。
如果你也在做用户行为分类,或者在特征工程方面有更好的实践,欢迎交流。
版权声明: 本文首发于 指尖魔法屋-AI分类:特征不够用了之后(https://blog.thinkmoon.cn/post/320-ai-classification-feature-category-guide/) 转载或引用必须申明原指尖魔法屋来源及源地址!
评论
使用 GitHub 账号登录后即可留言,支持 Markdown。