聊聊我在家撸Rust之余搞懂的机器学习那点事
早上八点钟,咖啡刚泡好,窗外鸟叫得挺欢。我坐在家里书房,打开Trae准备写点代码,结果发现昨晚跑的一个Rust小项目编译又报错了,满屏的lifetime错误看得我脑壳疼。索性先放一放,打开Moltbot跟它聊了两句,让它帮我理理思路。
聊着聊着就聊到了机器学习。说来也巧,最近公司有个新项目要上一个推荐模块,老板说"你顺便研究研究机器学习呗",得,又被安排了。其实我自己也一直想系统搞搞这块,毕竟现在不会点AI都不好意思说自己是搞技术的。
今天这篇文章,就跟大家聊聊我这段时间入门机器学习的一些心得。不讲太深的数学推导(那种东西看论文去),就聊聊基础概念,顺便对比对比各种算法的选型,给想入门的兄弟们一点参考。
先搞清楚:机器学习到底在干嘛
很多兄弟一听"机器学习"就觉得高深莫测,其实说白了就一句话:让程序自己从数据里找规律,而不是你手写规则。
举个例子,你要做一个垃圾邮件过滤器。传统做法是你写一堆if-else:如果标题包含"中奖"就标记为垃圾邮件,如果发件人在黑名单里也标记……写到天荒地老也写不完。机器学习的思路是:我给你一堆已经标好"垃圾/正常"的邮件样本,你自己去学,学完了遇到新邮件你就知道该咋分了。
这里面有几个核心概念得先搞清楚:
| 概念 | 大白话解释 | 举个栗子 |
|---|---|---|
| 特征(Feature) | 你用来判断的依据 | 邮件标题、发件人、正文关键词 |
| 标签(Label) | 你要预测的目标 | 是不是垃圾邮件 |
| 模型(Model) | 学到的规律 | 一套判断规则或函数 |
| 训练(Training) | 让模型学习的过程 | 喂数据,调参数 |
| 过拟合(Overfitting) | 学得太死了,换个数据就不行 | 做题只会背答案,换题型就懵 |
三大门派:监督、无监督、强化学习
机器学习算法大体上分三个门派,各有各的玩法:
监督学习(Supervised Learning) 是最常见的,就是你给数据打上标签,让模型学。分类问题(这是猫还是狗)和回归问题(明天股票多少钱)都属于这个门派。像线性回归、逻辑回归、决策树、SVM、随机森林这些都是。
无监督学习(Unsupervised Learning) 就比较野了,数据没标签,让模型自己去找结构。聚类(把相似的东西归到一堆)和降维(把高维数据压到低维还能保留主要信息)是典型代表。K-Means、DBSCAN、PCA这些是常用算法。
强化学习(Reinforcement Learning) 是另一个路子,模型通过不断试错来获取奖励,有点像训练小狗。AlphaGo就是这条路子的代表。这块水太深,入门阶段先不碰。
算法选型:到底该用哪个?
这才是大家最关心的问题。我最近做那个推荐模块的时候,也纠结了好久。下面是我整理的一个选型参考,结合了实际场景:
| 算法 | 适用场景 | 优点 | 缺点 | 上手难度 |
|---|---|---|---|---|
| 线性回归 | 连续值预测,特征和结果线性相关 | 简单、快、可解释 | 只能处理线性关系 | ★☆☆☆☆ |
| 逻辑回归 | 二分类问题 | 简单、输出概率 | 同样只能线性 | ★★☆☆☆ |
| 决策树 | 分类/回归,特征有明确阈值 | 可解释性强,不用特征缩放 | 容易过拟合 | ★★☆☆☆ |
| 随机森林 | 分类/回归,通用 | 不容易过拟合,精度高 | 模型大,慢 | ★★★☆☆ |
| SVM | 分类,特征维度高 | 小样本效果好 | 大数据集慢,调参痛苦 | ★★★★☆ |
| K-Means | 聚类,数据量大 | 快,简单 | 要指定K值,对异常值敏感 | ★★☆☆☆ |
| XGBoost | 表格数据,各种比赛 | 精度高,支持并行 | 调参是门玄学 | ★★★★☆ |
说点实际的。如果你刚入门,数据量不大,特征也不多,随机森林基本是万金油,先跑一个baseline再说。别一上来就搞深度学习,杀鸡用牛刀不说,你还调不明白。
我之前有个同事,拿到一个二分类问题,上来就搭了个神经网络,调了三天参数,准确率还不如我跑了十分钟的XGBoost。当时他那个表情,我现在想起来还想笑。
实战踩坑:推荐模块的那些事
回到我那个推荐模块的项目。需求是这样的:根据用户的历史行为(浏览、点击、收藏、购买),预测用户对某个商品的点击概率。本质上是个二分类问题。
数据方面,我们有一份大概50万条的用户行为记录,特征包括用户年龄、性别、历史浏览品类、商品类目、价格区间、上架时间等等,大概二十来个特征。
第一步:数据预处理
这一步其实最耗时,也最重要。圈里有句话叫"数据和特征决定了机器学习的上限,模型和算法只是逼近这个上限"。
import pandas as pd
from sklearn.preprocessing import LabelEncoder, StandardScaler
from sklearn.model_selection import train_test_split
# 加载数据
df = pd.read_csv('user_behavior.csv')
# 处理缺失值 - 这里我用了填充,有时候直接删也行
df['age'].fillna(df['age'].median(), inplace=True)
df['price'].fillna(0, inplace=True)
# 类别特征编码
label_encoders = {}
for col in ['gender', 'category', 'price_range']:
le = LabelEncoder()
df[col] = le.fit_transform(df[col].astype(str))
label_encoders[col] = le
# 特征缩放 - SVM和神经网络对缩放敏感,树模型倒无所谓
scaler = StandardScaler()
numeric_cols = ['age', 'price', 'browse_count']
df[numeric_cols] = scaler.fit_transform(df[numeric_cols])
# 划分训练集和测试集
X = df.drop('is_clicked', axis=1)
y = df['is_clicked']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
这里有个坑:一开始我没做特征缩放,直接跑SVM,效果奇差。后来加了StandardScaler,准确率直接涨了8个点。树模型虽然不怕这个,但如果你后面要对比多个模型,统一做一下缩放省事。
第二步:模型训练和对比
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, roc_auc_score, classification_report
import xgboost as xgb
models = {
'LogisticRegression': LogisticRegression(max_iter=1000),
'RandomForest': RandomForestClassifier(n_estimators=100, random_state=42),
'XGBoost': xgb.XGBClassifier(n_estimators=100, learning_rate=0.1, random_state=42)
}
results = {}
for name, model in models.items():
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
y_prob = model.predict_proba(X_test)[:, 1]
acc = accuracy_score(y_test, y_pred)
auc = roc_auc_score(y_test, y_prob)
results[name] = {'accuracy': acc, 'auc': auc}
print(f"{name}: Accuracy={acc:.4f}, AUC={auc:.4f}")
跑出来的结果:
| 模型 | Accuracy | AUC | 训练耗时 |
|---|---|---|---|
| LogisticRegression | 0.7231 | 0.7102 | 0.3s |
| RandomForest | 0.7856 | 0.8023 | 2.1s |
| XGBoost | 0.8102 | 0.8341 | 1.8s |
XGBoost赢了,但也没赢太多。这里有个经验:别只看准确率,分类问题一定要看AUC。特别是正负样本不平衡的时候(我们这个数据集点击率只有12%),准确率会骗人的。你全预测为"不点击",准确率都有88%,但那有啥用?
第三步:调参
XGBoost的调参确实是个玄学。我当时的策略是先粗调再细调:
from sklearn.model_selection import GridSearchCV
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [3, 5, 7],
'learning_rate': [0.01, 0.05, 0.1],
'subsample': [0.8, 1.0],
'colsample_bytree': [0.8, 1.0]
}
grid_search = GridSearchCV(
xgb.XGBClassifier(random_state=42),
param_grid,
cv=5,
scoring='roc_auc',
n_jobs=-1
)
grid_search.fit(X_train, y_train)
print(f"Best params: {grid_search.best_params_}")
print(f"Best AUC: {grid_search.best_score_:.4f}")
GridSearch跑了大概二十分钟,最终最优参数是 n_estimators=200, max_depth=5, learning_rate=0.05, subsample=0.8, colsample_bytree=0.8,AUC提升到了0.8456。
这里说个教训:一开始我max_depth设了10,训练集上AUC飙到0.95,测试集只有0.78,典型的过拟合。后来把深度降下来,加了subsample和colsample_bytree做随机采样,泛化能力才上来。
一些碎碎念的心得
搞了这段时间机器学习,有几点感受想跟大家分享:
1. 数据比算法重要一万倍。 你花80%的时间在清洗数据、构造特征上,这很正常。别觉得这是脏活累活就不愿意干,特征工程做好了,简单模型也能出好效果。
2. 先跑baseline,再优化。 别一上来就追求极致,先用最简单的模型跑个baseline,心里有个底。很多时候你会发现,逻辑回归就能解决80%的问题。
3. 评估指标要选对。 分类问题看AUC、F1-score,回归问题看RMSE、MAE。别只看准确率,特别是样本不平衡的时候。
4. 工具很重要。 我最近用Trae写代码,体验确实不错,自动补全很智能,写Python和Rust都挺顺手。另外我建了个知识库,把看过的论文笔记、踩过的坑都丢进去,用的时候直接问Moltbot,省得翻来翻去。说到这个,最近改简历的时候也把这段机器学习的经历加上了,毕竟现在面试不问点AI相关的都不好意思。
5. 别怕数学,但也别死磕数学。 入门阶段理解直觉就够了,知道这个算法在干嘛、适合什么场景。等你用熟了,再回头补数学,那时候理解会深很多。
好了,咖啡喝完了,Rust的编译错误还等着我呢。今天先聊到这,希望这篇入门指南对想学机器学习的兄弟有点帮助。
有什么想聊的,评论区见。我一般早上八点到晚上都在,远程办公嘛,时间比较自由。要是聊Rust的就更好了,最近被borrow checker折磨得够呛,急需同病相怜的兄弟一起吐槽。
Peace out ✌️


评论 0